This is an automated email from the ASF dual-hosted git repository.

tanxinyu 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 95972550d8c Introduce flushing on specific regions & IoTConsensusV2: 
Flush old leader when leader transfer & Delay the execution of 
invalidateSchemaCache after leader change (#14910)
95972550d8c is described below

commit 95972550d8ca02e714e4f30fd0044570bb6e8faa
Author: Peng Junzhi <[email protected]>
AuthorDate: Wed Feb 26 19:27:11 2025 +0800

    Introduce flushing on specific regions & IoTConsensusV2: Flush old leader 
when leader transfer & Delay the execution of invalidateSchemaCache after 
leader change (#14910)
    
    * flush on old leader if leader transfer
    
    * fix review
    
    * spotless
    
    * add log
    
    * fix typo
    
    * test conf
    
    * spotless
    
    * Revert "test conf"
    
    This reverts commit fc845910193d80facd9ff100920830a7e596787e.
    
    * fix review
    
    * late the execution of invalidate
    
    * fix review
    
    * add region leader broadcast
    
    * fix
    
    * fix review
    
    * fix
---
 .../iotdb/confignode/manager/ConfigManager.java    |   9 ++
 .../apache/iotdb/confignode/manager/IManager.java  |   3 +
 .../manager/load/balancer/RouteBalancer.java       | 152 ++++++++++++++++-----
 .../iotdb/confignode/manager/node/NodeManager.java |   8 ++
 .../iotdb/db/storageengine/StorageEngine.java      |  19 ++-
 .../thrift-commons/src/main/thrift/common.thrift   |   1 +
 6 files changed, 155 insertions(+), 37 deletions(-)

diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
index 5a319bacd35..ad584408a02 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
@@ -1639,6 +1639,15 @@ public class ConfigManager implements IManager {
         : status;
   }
 
+  @Override
+  public TSStatus flushOnSpecificDN(
+      final TFlushReq req, final Map<Integer, TDataNodeLocation> 
dataNodeLocationMap) {
+    final TSStatus status = confirmLeader();
+    return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()
+        ? RpcUtils.squashResponseStatusList(nodeManager.flushOnSpecificDN(req, 
dataNodeLocationMap))
+        : status;
+  }
+
   @Override
   public TSStatus clearCache(final Set<Integer> clearCacheOptions) {
     final TSStatus status = confirmLeader();
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java
index 498079237ae..3e6abee92e3 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java
@@ -572,6 +572,9 @@ public interface IManager {
   /** Flush on all DataNodes. */
   TSStatus flush(TFlushReq req);
 
+  /** Flush on specific Datanode. */
+  TSStatus flushOnSpecificDN(TFlushReq req, Map<Integer, TDataNodeLocation> 
dataNodeLocationMap);
+
   /** Clear cache on all DataNodes. */
   TSStatus clearCache(final Set<Integer> clearCacheOptions);
 
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RouteBalancer.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RouteBalancer.java
index f88302115cd..001568778cd 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RouteBalancer.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RouteBalancer.java
@@ -22,6 +22,7 @@ import org.apache.iotdb.common.rpc.thrift.TConsensusGroupId;
 import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType;
 import org.apache.iotdb.common.rpc.thrift.TDataNodeConfiguration;
 import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation;
+import org.apache.iotdb.common.rpc.thrift.TFlushReq;
 import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
 import org.apache.iotdb.commons.cluster.NodeStatus;
@@ -56,15 +57,19 @@ import org.apache.tsfile.utils.Pair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.BiConsumer;
 import java.util.stream.Collectors;
 
 /** The RouteBalancer guides the cluster RegionGroups' leader distribution and 
routing priority. */
@@ -114,11 +119,16 @@ public class RouteBalancer implements 
IClusterStatusSubscriber {
   private static final long BALANCE_RATIS_LEADER_FAILED_INTERVAL_IN_NS = 20 * 
1000L * 1000L * 1000L;
   private final Map<TConsensusGroupId, Long> lastFailedTimeForLeaderBalance;
 
+  private final Map<Integer, List<String>> lastBalancedOldLeaderId2RegionMap;
+  private Map<TConsensusGroupId, Integer> lastDataRegion2OldLeaderMap;
+  private Set<TConsensusGroupId> lastBalancedDataRegionSet;
+
   public RouteBalancer(IManager configManager) {
     this.configManager = configManager;
     this.priorityMapLock = new ReentrantReadWriteLock();
     this.regionPriorityMap = new TreeMap<>();
     this.lastFailedTimeForLeaderBalance = new TreeMap<>();
+    this.lastBalancedOldLeaderId2RegionMap = new ConcurrentHashMap<>();
 
     switch (CONF.getLeaderDistributionPolicy()) {
       case AbstractLeaderBalancer.GREEDY_POLICY:
@@ -178,20 +188,38 @@ public class RouteBalancer implements 
IClusterStatusSubscriber {
             return;
           }
 
-          if (newLeaderId != -1 && 
!newLeaderId.equals(currentLeaderMap.get(regionGroupId))) {
+          int oldLeaderId = currentLeaderMap.get(regionGroupId);
+          if (newLeaderId != -1 && !newLeaderId.equals(oldLeaderId)) {
             LOGGER.info(
                 "[LeaderBalancer] Try to change the leader of Region: {} to 
DataNode: {} ",
                 regionGroupId,
                 newLeaderId);
             switch (consensusProtocolClass) {
-              case ConsensusFactory.IOT_CONSENSUS_V2:
               case ConsensusFactory.IOT_CONSENSUS:
               case ConsensusFactory.SIMPLE_CONSENSUS:
-                // For IoTConsensus or SimpleConsensus or PipeConsensus 
protocol, change
+                // For IoTConsensus or SimpleConsensus protocol, change
                 // RegionRouteMap is enough
                 successTransferMap.put(
                     regionGroupId, new 
ConsensusGroupHeartbeatSample(currentTime, newLeaderId));
                 break;
+              case ConsensusFactory.IOT_CONSENSUS_V2:
+                // For IoTConsensusV2 protocol, change RegionRouteMap and 
execute flush on old
+                // region leader
+                successTransferMap.put(
+                    regionGroupId, new 
ConsensusGroupHeartbeatSample(currentTime, newLeaderId));
+                // Prepare data for flushOldLeader
+                lastBalancedOldLeaderId2RegionMap.compute(
+                    oldLeaderId,
+                    (k, v) -> {
+                      if (v == null) {
+                        List<String> value = new ArrayList<>();
+                        value.add(String.valueOf(regionGroupId.getId()));
+                        return value;
+                      }
+                      v.add(String.valueOf(regionGroupId.getId()));
+                      return v;
+                    });
+                break;
               case ConsensusFactory.RATIS_CONSENSUS:
               default:
                 // For ratis protocol, the ConfigNode-leader will send a 
changeLeaderRequest to the
@@ -246,46 +274,97 @@ public class RouteBalancer implements 
IClusterStatusSubscriber {
 
     getLoadManager().forceUpdateConsensusGroupCache(successTransferMap);
 
-    invalidateSchemaCacheOfOldLeaders(currentLeaderMap, 
successTransferMap.keySet());
+    // Prepare data for invalidSchemaCacheOfOldLeaders
+    if (regionGroupType.equals(TConsensusGroupType.DataRegion)) {
+      lastBalancedDataRegionSet = successTransferMap.keySet();
+      lastDataRegion2OldLeaderMap = currentLeaderMap;
+    }
   }
 
-  private void invalidateSchemaCacheOfOldLeaders(
-      final Map<TConsensusGroupId, Integer> oldLeaderMap,
-      final Set<TConsensusGroupId> successTransferSet) {
-    final DataNodeAsyncRequestContext<String, TSStatus> 
invalidateSchemaCacheRequestHandler =
-        new 
DataNodeAsyncRequestContext<>(CnToDnAsyncRequestType.INVALIDATE_LAST_CACHE);
-    final AtomicInteger requestIndex = new AtomicInteger(0);
-    oldLeaderMap.entrySet().stream()
-        .filter(entry -> TConsensusGroupType.DataRegion == 
entry.getKey().getType())
-        .filter(entry -> successTransferSet.contains(entry.getKey()))
-        .forEach(
-            entry -> {
-              // set target
-              final Integer dataNodeId = entry.getValue();
-              if (dataNodeId == -1) {
-                return;
-              }
-              final TDataNodeLocation dataNodeLocation =
-                  
getNodeManager().getRegisteredDataNode(dataNodeId).getLocation();
-              if (dataNodeLocation == null) {
-                LOGGER.warn("DataNodeLocation is null, datanodeId {}", 
dataNodeId);
-                return;
-              }
-              invalidateSchemaCacheRequestHandler.putNodeLocation(
-                  requestIndex.get(), dataNodeLocation);
-              // set req
-              final TConsensusGroupId consensusGroupId = entry.getKey();
-              final String database = 
getPartitionManager().getRegionDatabase(consensusGroupId);
-              
invalidateSchemaCacheRequestHandler.putRequest(requestIndex.get(), database);
-              requestIndex.incrementAndGet();
-            });
-    CnToDnInternalServiceAsyncRequestManager.getInstance()
-        .sendAsyncRequest(invalidateSchemaCacheRequestHandler);
+  private void invalidateSchemaCacheOfOldLeaders() {
+    BiConsumer<Map<TConsensusGroupId, Integer>, Set<TConsensusGroupId>> 
consumer =
+        (oldLeaderMap, successTransferSet) -> {
+          final DataNodeAsyncRequestContext<String, TSStatus> 
invalidateSchemaCacheRequestHandler =
+              new 
DataNodeAsyncRequestContext<>(CnToDnAsyncRequestType.INVALIDATE_LAST_CACHE);
+          final AtomicInteger requestIndex = new AtomicInteger(0);
+          oldLeaderMap.entrySet().stream()
+              .filter(entry -> successTransferSet.contains(entry.getKey()))
+              .forEach(
+                  entry -> {
+                    // set target
+                    final Integer dataNodeId = entry.getValue();
+                    if (dataNodeId == -1) {
+                      return;
+                    }
+                    final TDataNodeLocation dataNodeLocation =
+                        
getNodeManager().getRegisteredDataNode(dataNodeId).getLocation();
+                    if (dataNodeLocation == null) {
+                      LOGGER.warn("DataNodeLocation is null, datanodeId {}", 
dataNodeId);
+                      return;
+                    }
+                    invalidateSchemaCacheRequestHandler.putNodeLocation(
+                        requestIndex.get(), dataNodeLocation);
+                    // set req
+                    final TConsensusGroupId consensusGroupId = entry.getKey();
+                    final String database =
+                        
getPartitionManager().getRegionDatabase(consensusGroupId);
+                    
invalidateSchemaCacheRequestHandler.putRequest(requestIndex.get(), database);
+                    requestIndex.incrementAndGet();
+                  });
+          CnToDnInternalServiceAsyncRequestManager.getInstance()
+              .sendAsyncRequest(invalidateSchemaCacheRequestHandler);
+        };
+
+    if (IS_ENABLE_AUTO_LEADER_BALANCE_FOR_DATA_REGION) {
+      consumer.accept(lastDataRegion2OldLeaderMap, lastBalancedDataRegionSet);
+    }
+  }
+
+  private void flushOldLeaderIfIoTV2() {
+    if (!IS_ENABLE_AUTO_LEADER_BALANCE_FOR_DATA_REGION
+        || !Objects.equals(
+            DATA_REGION_CONSENSUS_PROTOCOL_CLASS, 
ConsensusFactory.IOT_CONSENSUS_V2)) {
+      return;
+    }
+
+    BiConsumer<Integer, List<String>> consumer =
+        (oldLeaderId, regionGroupIds) -> {
+          TDataNodeConfiguration configuration =
+              getNodeManager().getRegisteredDataNode(oldLeaderId);
+          Map<Integer, TDataNodeLocation> oldLeaderDataNodeLocation = new 
HashMap<>();
+          oldLeaderDataNodeLocation.put(
+              configuration.getLocation().dataNodeId, 
configuration.getLocation());
+
+          TFlushReq flushReq = new TFlushReq();
+          flushReq.setRegionIds(regionGroupIds);
+          // Do our best to flush. If flush failed, never retry
+          TSStatus result = configManager.flushOnSpecificDN(flushReq, 
oldLeaderDataNodeLocation);
+          if (result.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) 
{
+            LOGGER.info(
+                "[IoTConsensusV2 Leader Changed] Successfully flush old leader 
{} for region {}",
+                oldLeaderId,
+                regionGroupIds);
+          } else {
+            LOGGER.info(
+                "[IoTConsensusV2 Leader Changed] Failed to flush old leader {} 
for region {}",
+                oldLeaderId,
+                regionGroupIds);
+          }
+        };
+    lastBalancedOldLeaderId2RegionMap.forEach(consumer);
+    // after flush, clear map for next balance
+    lastBalancedOldLeaderId2RegionMap.clear();
+  }
+
+  private synchronized void handleBalanceAction() {
+    invalidateSchemaCacheOfOldLeaders();
+    flushOldLeaderIfIoTV2();
   }
 
   public synchronized void balanceRegionLeaderAndPriority() {
     balanceRegionLeader();
     balanceRegionPriority();
+    handleBalanceAction();
   }
 
   /** Balance cluster RegionGroup route priority through configured algorithm. 
*/
@@ -468,5 +547,6 @@ public class RouteBalancer implements 
IClusterStatusSubscriber {
   public void 
onConsensusGroupStatisticsChanged(ConsensusGroupStatisticsChangeEvent event) {
     balanceRegionLeader();
     balanceRegionPriority();
+    handleBalanceAction();
   }
 }
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/node/NodeManager.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/node/NodeManager.java
index 3bdfcb35fef..ae94131f5a7 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/node/NodeManager.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/node/NodeManager.java
@@ -878,6 +878,14 @@ public class NodeManager {
     return clientHandler.getResponseList();
   }
 
+  public List<TSStatus> flushOnSpecificDN(
+      TFlushReq req, Map<Integer, TDataNodeLocation> dataNodeLocationMap) {
+    DataNodeAsyncRequestContext<TFlushReq, TSStatus> clientHandler =
+        new DataNodeAsyncRequestContext<>(CnToDnAsyncRequestType.FLUSH, req, 
dataNodeLocationMap);
+    
CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
+    return clientHandler.getResponseList();
+  }
+
   public List<TSStatus> clearCache(final Set<Integer> clearCacheOptions) {
     final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
         configManager.getNodeManager().getRegisteredDataNodeLocations();
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java
index c8c3f3b0383..f1f188fcb09 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java
@@ -530,6 +530,21 @@ public class StorageEngine implements IService {
     checkResults(tasks, "Failed to sync close processor.");
   }
 
+  public void syncCloseProcessorsInRegion(List<String> dataRegionIds) {
+    List<Future<Void>> tasks = new ArrayList<>();
+    for (DataRegion dataRegion : dataRegionMap.values()) {
+      if (dataRegion != null && 
dataRegionIds.contains(dataRegion.getDataRegionId())) {
+        tasks.add(
+            cachedThreadPool.submit(
+                () -> {
+                  dataRegion.syncCloseAllWorkingTsFileProcessors();
+                  return null;
+                }));
+      }
+    }
+    checkResults(tasks, "Failed to sync close processor.");
+  }
+
   public void syncCloseProcessorsInDatabase(String databaseName, boolean 
isSeq) {
     List<Future<Void>> tasks = new ArrayList<>();
     for (DataRegion dataRegion : dataRegionMap.values()) {
@@ -638,7 +653,9 @@ public class StorageEngine implements IService {
   }
 
   public void operateFlush(TFlushReq req) {
-    if (req.storageGroups == null || req.storageGroups.isEmpty()) {
+    if (req.getRegionIds() != null && !req.getRegionIds().isEmpty()) {
+      
StorageEngine.getInstance().syncCloseProcessorsInRegion(req.getRegionIds());
+    } else if (req.storageGroups == null || req.storageGroups.isEmpty()) {
       StorageEngine.getInstance().syncCloseAllProcessor();
       WALManager.getInstance().syncDeleteOutdatedFilesInWALNodes();
     } else {
diff --git a/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift 
b/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift
index 93eafedc118..8c2b8cedb46 100644
--- a/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift
+++ b/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift
@@ -119,6 +119,7 @@ enum TRegionMaintainTaskStatus {
 struct TFlushReq {
    1: optional string isSeq
    2: optional list<string> storageGroups
+   3: optional list<string> regionIds
 }
 
 struct TSettleReq {

Reply via email to