This is an automated email from the ASF dual-hosted git repository.
CRZbulabula pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new f028736f105 Optimize ConfigNode region lookups and reduce metadata
copying (#18902)
f028736f105 is described below
commit f028736f105fc0c79e56d8e1ce13b6b268e3a077
Author: d-wang-commit <[email protected]>
AuthorDate: Mon Oct 19 19:50:38 2026 +0800
Optimize ConfigNode region lookups and reduce metadata copying (#18902)
Look up the target Region and replica node IDs before copying their
metadata.
Read leaders directly from the cache instead of building the full leader
map.
---
.../iotdb/confignode/manager/load/LoadManager.java | 9 +
.../confignode/manager/load/cache/LoadCache.java | 10 ++
.../persistence/partition/PartitionInfo.java | 8 +-
.../procedure/env/RegionMaintainHandler.java | 36 ++--
.../load/cache/ConsensusGroupCacheTest.java | 28 +++
.../procedure/env/RegionMaintainHandlerTest.java | 85 ++++++---
.../procedure/env/RegionReplicaSetLookupTest.java | 195 +++++++++++++++++++++
7 files changed, 334 insertions(+), 37 deletions(-)
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java
index d5d7085bfa8..ed4d4d9be8a 100644
---
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java
@@ -501,6 +501,15 @@ public class LoadManager {
eventService.checkAndBroadcastConsensusGroupStatisticsChangeEventIfNecessary();
}
+ /**
+ * Get the latest leader of the specified RegionGroup.
+ *
+ * @return the leader ID, or -1 if the cache is absent or no leader has been
selected
+ */
+ public int getRegionLeaderId(TConsensusGroupId regionGroupId) {
+ return loadCache.getRegionLeaderId(regionGroupId);
+ }
+
/**
* Get the latest RegionLeaderMap.
*
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/cache/LoadCache.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/cache/LoadCache.java
index b7cf0639f3d..502b018fd59 100644
---
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/cache/LoadCache.java
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/cache/LoadCache.java
@@ -799,6 +799,16 @@ public class LoadCache {
consensusGroupCacheMap.remove(consensusGroupId);
}
+ /**
+ * Get the latest leader of the specified RegionGroup.
+ *
+ * @return the leader ID, or -1 if the cache is absent or no leader has been
selected
+ */
+ public int getRegionLeaderId(TConsensusGroupId regionGroupId) {
+ ConsensusGroupCache cache = consensusGroupCacheMap.get(regionGroupId);
+ return cache == null ? ConsensusGroupCache.UN_READY_LEADER_ID :
cache.getLeaderId();
+ }
+
/**
* Safely get the latest RegionLeaderMap.
*
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/partition/PartitionInfo.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/partition/PartitionInfo.java
index 7459510f4a2..3c990a89578 100644
---
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/partition/PartitionInfo.java
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/partition/PartitionInfo.java
@@ -791,11 +791,9 @@ public class PartitionInfo implements SnapshotProcessor {
*/
public List<TRegionReplicaSet> getReplicaSets(
String database, List<TConsensusGroupId> regionGroupIds) {
- if (databasePartitionTables.containsKey(database)) {
- return
databasePartitionTables.get(database).getReplicaSets(regionGroupIds);
- } else {
- return Collections.emptyList();
- }
+ // A database can be deleted while a Region lookup is in progress.
+ DatabasePartitionTable table = databasePartitionTables.get(database);
+ return table == null ? Collections.emptyList() :
table.getReplicaSets(regionGroupIds);
}
/**
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandler.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandler.java
index f378ef13218..ea08fbbb6c6 100644
---
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandler.java
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandler.java
@@ -53,6 +53,7 @@ import org.apache.iotdb.confignode.i18n.ProcedureMessages;
import org.apache.iotdb.confignode.manager.ConfigManager;
import org.apache.iotdb.confignode.manager.load.LoadManager;
import
org.apache.iotdb.confignode.manager.load.cache.consensus.ConsensusGroupHeartbeatSample;
+import org.apache.iotdb.confignode.manager.partition.PartitionManager;
import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
import org.apache.iotdb.consensus.pipe.consensuspipe.ConsensusPipeName;
@@ -72,6 +73,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -771,9 +773,17 @@ public class RegionMaintainHandler {
}
public Optional<TRegionReplicaSet> getRegionReplicaSet(TConsensusGroupId
regionId) {
- return configManager.getPartitionManager().getAllReplicaSets().stream()
- .filter(rg -> rg.regionId.equals(regionId))
- .findAny();
+ if (regionId == null) {
+ return Optional.empty();
+ }
+ PartitionManager partitionManager = configManager.getPartitionManager();
+ String database = partitionManager.getRegionDatabase(regionId);
+ if (database == null) {
+ return Optional.empty();
+ }
+ // Locate by Region ID before copying: unrelated replica sets need not be
locked or copied.
+ return partitionManager.getReplicaSets(database,
Collections.singletonList(regionId)).stream()
+ .findFirst();
}
public String getRegionReplicaSetString(TConsensusGroupId regionId) {
@@ -931,11 +941,15 @@ public class RegionMaintainHandler {
return Optional.empty();
}
- // Choosing the RUNNING DataNodes to execute firstly
- // If all DataNodes are not RUNNING, then choose the REMOVING DataNodes
secondly
+ Set<Integer> regionDataNodeIds =
+
regionLocations.stream().map(TDataNodeLocation::getDataNodeId).collect(Collectors.toSet());
+ // Filter IDs before reading configurations, so unrelated DataNodes are
not deep-copied.
List<TDataNodeLocation> aliveDataNodes =
-
configManager.getNodeManager().filterDataNodeThroughStatus(allowingStatus).stream()
- .map(TDataNodeConfiguration::getLocation)
+
configManager.getLoadManager().filterDataNodeThroughStatus(allowingStatus).stream()
+ .filter(regionDataNodeIds::contains)
+ .map(
+ nodeId ->
+
configManager.getNodeManager().getRegisteredDataNode(nodeId).getLocation())
.collect(Collectors.toList());
final int leaderId = getRegionLeaderId(regionId);
Collections.shuffle(aliveDataNodes);
@@ -962,11 +976,11 @@ public class RegionMaintainHandler {
*/
private int getRegionLeaderId(TConsensusGroupId regionId) {
final LoadManager loadManager = configManager.getLoadManager();
- Integer leaderId = loadManager.getRegionLeaderMap().get(regionId);
- if (leaderId == null || leaderId == -1) {
+ int leaderId = loadManager.getRegionLeaderId(regionId);
+ if (leaderId == -1) {
loadManager.waitForRegionGroupReady(Collections.singletonList(regionId));
- leaderId = loadManager.getRegionLeaderMap().get(regionId);
+ leaderId = loadManager.getRegionLeaderId(regionId);
}
- return leaderId == null ? -1 : leaderId;
+ return leaderId;
}
}
diff --git
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/cache/ConsensusGroupCacheTest.java
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/cache/ConsensusGroupCacheTest.java
index 7bbd366be57..91bd3c020ee 100644
---
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/cache/ConsensusGroupCacheTest.java
+++
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/cache/ConsensusGroupCacheTest.java
@@ -18,14 +18,42 @@
*/
package org.apache.iotdb.confignode.manager.load.cache;
+import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId;
+import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
import
org.apache.iotdb.confignode.manager.load.cache.consensus.ConsensusGroupCache;
import
org.apache.iotdb.confignode.manager.load.cache.consensus.ConsensusGroupHeartbeatSample;
import org.junit.Assert;
import org.junit.Test;
+import java.util.Collections;
+
public class ConsensusGroupCacheTest {
+ @Test
+ public void regionLeaderLookupTest() {
+ LoadCache loadCache = new LoadCache();
+ TConsensusGroupId dataRegion = new
TConsensusGroupId(TConsensusGroupType.DataRegion, 1);
+ TConsensusGroupId schemaRegion = new
TConsensusGroupId(TConsensusGroupType.SchemaRegion, 1);
+ Assert.assertEquals(-1, loadCache.getRegionLeaderId(dataRegion));
+ Assert.assertEquals(-1, loadCache.getRegionLeaderId(schemaRegion));
+
+ loadCache.createRegionGroupHeartbeatCache("root.test", dataRegion,
Collections.singleton(2));
+ loadCache.createRegionGroupHeartbeatCache("root.test", schemaRegion,
Collections.singleton(3));
+ Assert.assertEquals(-1, loadCache.getRegionLeaderId(dataRegion));
+ Assert.assertEquals(-1, loadCache.getRegionLeaderId(schemaRegion));
+
+ loadCache.cacheConsensusSample(dataRegion, new
ConsensusGroupHeartbeatSample(1L, 2));
+ loadCache.cacheConsensusSample(schemaRegion, new
ConsensusGroupHeartbeatSample(1L, 3));
+ loadCache.updateConsensusGroupStatistics();
+ Assert.assertEquals(2, loadCache.getRegionLeaderId(dataRegion));
+ Assert.assertEquals(3, loadCache.getRegionLeaderId(schemaRegion));
+
+ loadCache.removeRegionGroupCache(dataRegion);
+ Assert.assertEquals(-1, loadCache.getRegionLeaderId(dataRegion));
+ Assert.assertEquals(3, loadCache.getRegionLeaderId(schemaRegion));
+ }
+
@Test
public void periodicUpdateTest() {
ConsensusGroupCache consensusGroupCache = new ConsensusGroupCache();
diff --git
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandlerTest.java
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandlerTest.java
index b94c9625968..82a71b58b68 100644
---
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandlerTest.java
+++
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/env/RegionMaintainHandlerTest.java
@@ -91,6 +91,7 @@ public class RegionMaintainHandlerTest {
private TConsensusGroupId regionId;
private LoadManager loadManager;
+ private NodeManager nodeManager;
private RegionMaintainHandler handler;
private String originalConsensusProtocol;
@@ -104,25 +105,26 @@ public class RegionMaintainHandlerTest {
regionId = new TConsensusGroupId(regionType, 1);
ConfigManager configManager = mock(ConfigManager.class);
- NodeManager nodeManager = mock(NodeManager.class);
+ nodeManager = mock(NodeManager.class);
PartitionManager partitionManager = mock(PartitionManager.class);
loadManager = mock(LoadManager.class);
when(configManager.getNodeManager()).thenReturn(nodeManager);
when(configManager.getPartitionManager()).thenReturn(partitionManager);
when(configManager.getLoadManager()).thenReturn(loadManager);
- when(partitionManager.getAllReplicaSets())
+ when(partitionManager.getRegionDatabase(regionId)).thenReturn("root.test");
+ when(partitionManager.getReplicaSets("root.test",
Collections.singletonList(regionId)))
.thenReturn(
Collections.singletonList(
new TRegionReplicaSet(
regionId, Arrays.asList(original, coordinator,
otherReplica))));
- when(nodeManager.filterDataNodeThroughStatus(NodeStatus.Running,
NodeStatus.ReadOnly))
- .thenReturn(
- Arrays.asList(
- new TDataNodeConfiguration().setLocation(original),
- new TDataNodeConfiguration().setLocation(coordinator),
- new TDataNodeConfiguration().setLocation(otherReplica),
- new TDataNodeConfiguration().setLocation(nonReplica)));
- when(loadManager.getRegionLeaderMap()).thenReturn(Collections.emptyMap());
+ when(loadManager.filterDataNodeThroughStatus(NodeStatus.Running,
NodeStatus.ReadOnly))
+ .thenReturn(Arrays.asList(1, 2, 3, 4));
+ for (TDataNodeLocation location :
+ Arrays.asList(original, coordinator, otherReplica, nonReplica)) {
+ when(nodeManager.getRegisteredDataNode(location.getDataNodeId()))
+ .thenReturn(new TDataNodeConfiguration().setLocation(location));
+ }
+ when(loadManager.getRegionLeaderId(regionId)).thenReturn(-1);
handler = new RegionMaintainHandler(configManager);
}
@@ -144,7 +146,7 @@ public class RegionMaintainHandlerTest {
@Test
public void testFilterReplicaWithUnknownLeader() {
-
when(loadManager.getRegionLeaderMap()).thenReturn(Collections.singletonMap(regionId,
-1));
+ when(loadManager.getRegionLeaderId(regionId)).thenReturn(-1);
assertEquals(
Optional.of(otherReplica),
handler.filterDataNodeWithOtherRegionReplica(
@@ -154,8 +156,7 @@ public class RegionMaintainHandlerTest {
@Test
public void testFilterReplicaPrefersKnownLeader() {
- when(loadManager.getRegionLeaderMap())
- .thenReturn(Collections.singletonMap(regionId,
otherReplica.getDataNodeId()));
+
when(loadManager.getRegionLeaderId(regionId)).thenReturn(otherReplica.getDataNodeId());
assertEquals(
Optional.of(otherReplica),
handler.filterDataNodeWithOtherRegionReplica(regionId, original));
@@ -178,17 +179,15 @@ public class RegionMaintainHandlerTest {
@Test
public void testTransferLeaderWithUnknownLeader() throws Exception {
-
when(loadManager.getRegionLeaderMap()).thenReturn(Collections.singletonMap(regionId,
-1));
+ when(loadManager.getRegionLeaderId(regionId)).thenReturn(-1);
assertLeaderTransfer();
}
@Test
public void testTransferLeaderWaitsForLeaderElection() throws Exception {
// The filter has a known leader, while the transfer lookup observes a
stale cache first.
- when(loadManager.getRegionLeaderMap())
- .thenReturn(Collections.singletonMap(regionId,
otherReplica.getDataNodeId()))
- .thenReturn(Collections.emptyMap())
- .thenReturn(Collections.singletonMap(regionId,
original.getDataNodeId()));
+ when(loadManager.getRegionLeaderId(regionId))
+ .thenReturn(otherReplica.getDataNodeId(), -1,
original.getDataNodeId());
assertLeaderTransfer();
verify(loadManager).waitForRegionGroupReady(Collections.singletonList(regionId));
}
@@ -196,9 +195,7 @@ public class RegionMaintainHandlerTest {
@Test
public void testTransferLeaderWhenCacheEntryDisappears() throws Exception {
// The cache can change between replica selection and the transfer's
leader lookup.
- when(loadManager.getRegionLeaderMap())
- .thenReturn(Collections.singletonMap(regionId, -1))
- .thenReturn(Collections.emptyMap());
+
when(loadManager.getRegionLeaderId(regionId)).thenReturn(otherReplica.getDataNodeId(),
-1);
assertLeaderTransfer();
}
@@ -239,6 +236,52 @@ public class RegionMaintainHandlerTest {
}
}
+ @Test
+ public void testFilterReplicaDoesNotReadUnrelatedConfigurations() {
+ assertEquals(
+ Optional.of(otherReplica),
+ handler.filterDataNodeWithOtherRegionReplica(
+ regionId, Arrays.asList(original, coordinator)));
+ verify(loadManager, never()).getRegionLeaderMap();
+ verify(nodeManager,
never()).getRegisteredDataNode(nonReplica.getDataNodeId());
+ verify(nodeManager, never())
+ .filterDataNodeThroughStatus(NodeStatus.Running, NodeStatus.ReadOnly);
+ }
+
+ @Test
+ public void testFilterReplicaSkipsUnregisteredNode() {
+ when(nodeManager.getRegisteredDataNode(otherReplica.getDataNodeId()))
+ .thenReturn(new TDataNodeConfiguration());
+ assertFalse(
+ handler
+ .filterDataNodeWithOtherRegionReplica(regionId,
Arrays.asList(original, coordinator))
+ .isPresent());
+ }
+
+ @Test
+ public void testFilterReplicaRejectsChangedEndpoint() {
+ TDataNodeLocation changedLocation = otherReplica.deepCopy();
+ changedLocation.setInternalEndPoint(new TEndPoint("127.0.0.2", 10730));
+ when(nodeManager.getRegisteredDataNode(otherReplica.getDataNodeId()))
+ .thenReturn(new TDataNodeConfiguration().setLocation(changedLocation));
+ assertFalse(
+ handler
+ .filterDataNodeWithOtherRegionReplica(regionId,
Arrays.asList(original, coordinator))
+ .isPresent());
+ }
+
+ @Test
+ public void testFilterReplicaPreservesRequestedStatuses() {
+ when(loadManager.filterDataNodeThroughStatus(NodeStatus.Removing))
+ .thenReturn(Collections.singletonList(otherReplica.getDataNodeId()));
+ assertEquals(
+ Optional.of(otherReplica),
+ handler.filterDataNodeWithOtherRegionReplica(regionId, original,
NodeStatus.Removing));
+ verify(nodeManager,
never()).getRegisteredDataNode(coordinator.getDataNodeId());
+ verify(loadManager, never()).getRegionLeaderMap();
+ verify(nodeManager,
never()).getRegisteredDataNode(nonReplica.getDataNodeId());
+ }
+
private RegionMaintainHandler stubStatusAndSubmit(
final NodeStatus status, final TSStatus submitResult) {
RegionMaintainHandler retryHandler = spy(handler);
diff --git
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/env/RegionReplicaSetLookupTest.java
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/env/RegionReplicaSetLookupTest.java
new file mode 100644
index 00000000000..2b91f3532f1
--- /dev/null
+++
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/env/RegionReplicaSetLookupTest.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.confignode.procedure.env;
+
+import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId;
+import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
+import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation;
+import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
+import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType;
+import
org.apache.iotdb.confignode.consensus.request.write.database.DatabaseSchemaPlan;
+import
org.apache.iotdb.confignode.consensus.request.write.database.DeleteDatabasePlan;
+import
org.apache.iotdb.confignode.consensus.request.write.database.PreDeleteDatabasePlan;
+import
org.apache.iotdb.confignode.consensus.request.write.region.CreateRegionGroupsPlan;
+import org.apache.iotdb.confignode.manager.ConfigManager;
+import org.apache.iotdb.confignode.manager.partition.PartitionManager;
+import org.apache.iotdb.confignode.persistence.partition.PartitionInfo;
+import org.apache.iotdb.confignode.rpc.thrift.TDatabaseSchema;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(Parameterized.class)
+public class RegionReplicaSetLookupTest {
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> regionTypes() {
+ return Arrays.asList(
+ new Object[] {TConsensusGroupType.DataRegion},
+ new Object[] {TConsensusGroupType.SchemaRegion});
+ }
+
+ @Parameterized.Parameter public TConsensusGroupType regionType;
+
+ private final List<CountingReplicaSet> replicaSets = new ArrayList<>();
+ private PartitionInfo partitionInfo;
+ private PartitionManager partitionManager;
+ private RegionMaintainHandler handler;
+ private TConsensusGroupId target;
+
+ @Before
+ public void setUp() {
+ partitionInfo = new PartitionInfo();
+ partitionManager = mock(PartitionManager.class);
+ ConfigManager configManager = mock(ConfigManager.class);
+ when(configManager.getPartitionManager()).thenReturn(partitionManager);
+ when(partitionManager.getRegionDatabase(any()))
+ .thenAnswer(invocation ->
partitionInfo.getRegionDatabase(invocation.getArgument(0)));
+ when(partitionManager.getReplicaSets(anyString(), anyList()))
+ .thenAnswer(
+ invocation ->
+ partitionInfo.getReplicaSets(invocation.getArgument(0),
invocation.getArgument(1)));
+ when(partitionManager.getAllReplicaSets())
+ .thenAnswer(invocation -> partitionInfo.getAllReplicaSets());
+ handler = new RegionMaintainHandler(configManager);
+ for (int database = 0; database < 3; database++) {
+ String name = "root.lookup" + database;
+ partitionInfo.createDatabase(
+ new DatabaseSchemaPlan(ConfigPhysicalPlanType.CreateDatabase, new
TDatabaseSchema(name)));
+ CreateRegionGroupsPlan plan = new CreateRegionGroupsPlan();
+ for (int index = 0; index < 16; index++) {
+ for (TConsensusGroupType type :
+ Arrays.asList(TConsensusGroupType.DataRegion,
TConsensusGroupType.SchemaRegion)) {
+ CountingReplicaSet replicaSet =
+ new CountingReplicaSet(new TConsensusGroupId(type, database * 16
+ index));
+ replicaSets.add(replicaSet);
+ plan.addRegionGroup(name, replicaSet);
+ }
+ }
+ partitionInfo.createRegionGroups(plan);
+ }
+ target = new TConsensusGroupId(regionType, 20);
+ }
+
+ @Test
+ public void testLookupCopiesOnlyRequestedRegion() {
+ Optional<TRegionReplicaSet> result = handler.getRegionReplicaSet(target);
+ assertTrue(result.isPresent());
+ assertEquals(target, result.get().getRegionId());
+ assertEquals(1, copyCount());
+ for (CountingReplicaSet replicaSet : replicaSets) {
+ assertEquals(replicaSet.getRegionId().equals(target) ? 1 : 0,
replicaSet.copies);
+ }
+ }
+
+ @Test
+ public void testReturnedReplicaSetIsAnIndependentDeepCopy() {
+ TRegionReplicaSet first = handler.getRegionReplicaSet(target).get();
+ first.getDataNodeLocations().get(0).getInternalEndPoint().setIp("changed");
+ first.getDataNodeLocations().remove(1);
+ first.getRegionId().setId(-1);
+ TRegionReplicaSet second = handler.getRegionReplicaSet(target).get();
+ assertEquals(target, second.getRegionId());
+ assertEquals(3, second.getDataNodeLocationsSize());
+ assertEquals("127.0.0.1",
second.getDataNodeLocations().get(0).getInternalEndPoint().getIp());
+ assertNotSame(first.getDataNodeLocations().get(0),
second.getDataNodeLocations().get(0));
+ assertEquals(2, copyCount());
+ }
+
+ @Test
+ public void testMissingRegionDoesNotCopyReplicaSets() {
+ TConsensusGroupId missing = new TConsensusGroupId(regionType, 9999);
+ assertFalse(handler.getRegionReplicaSet(missing).isPresent());
+ assertTrue(handler.findRegionLocations(missing).isEmpty());
+ assertEquals("UNKNOWN!", handler.getRegionReplicaSetString(missing));
+ assertEquals(0, copyCount());
+ }
+
+ @Test
+ public void testNullRegionRemainsAbsent() {
+ assertFalse(handler.getRegionReplicaSet(null).isPresent());
+ assertEquals(0, copyCount());
+ }
+
+ @Test
+ public void testPreDeletedDatabaseRemainsVisible() {
+ partitionInfo.preDeleteDatabase(
+ new PreDeleteDatabasePlan("root.lookup1",
PreDeleteDatabasePlan.PreDeleteType.EXECUTE));
+ assertTrue(handler.getRegionReplicaSet(target).isPresent());
+ assertEquals(1, copyCount());
+ }
+
+ @Test
+ public void testDatabaseDeletedBetweenLookups() {
+ when(partitionManager.getRegionDatabase(target))
+ .thenAnswer(
+ invocation -> {
+ String database = partitionInfo.getRegionDatabase(target);
+ partitionInfo.deleteDatabase(new DeleteDatabasePlan(database));
+ return database;
+ });
+ assertFalse(handler.getRegionReplicaSet(target).isPresent());
+ assertEquals(0, copyCount());
+ }
+
+ private int copyCount() {
+ return replicaSets.stream().mapToInt(replicaSet ->
replicaSet.copies).sum();
+ }
+
+ private static class CountingReplicaSet extends TRegionReplicaSet {
+ private int copies;
+
+ private CountingReplicaSet(TConsensusGroupId regionId) {
+ setRegionId(regionId);
+ List<TDataNodeLocation> locations = new ArrayList<>();
+ for (int nodeId = 0; nodeId < 3; nodeId++) {
+ locations.add(
+ new TDataNodeLocation()
+ .setDataNodeId(nodeId)
+ .setInternalEndPoint(new TEndPoint("127.0.0.1", 10730 +
nodeId)));
+ }
+ setDataNodeLocations(locations);
+ }
+
+ @Override
+ public TRegionReplicaSet deepCopy() {
+ copies++;
+ return super.deepCopy();
+ }
+ }
+}