You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by qi...@apache.org on 2022/08/26 04:05:52 UTC

[iotdb] branch master updated: [IOTDB-4206] Record DataNode's read-only status in ConfigNode (#7108)

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

qiaojialin 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 37d6cfed32 [IOTDB-4206] Record DataNode's read-only status in ConfigNode (#7108)
37d6cfed32 is described below

commit 37d6cfed32b4aa2858c898dcb012b824f3ab041e
Author: YongzaoDan <33...@users.noreply.github.com>
AuthorDate: Fri Aug 26 12:05:46 2022 +0800

    [IOTDB-4206] Record DataNode's read-only status in ConfigNode (#7108)
---
 .../async/handlers/DataNodeHeartbeatHandler.java   |  2 +-
 .../iotdb/confignode/manager/NodeManager.java      |  4 +-
 .../iotdb/confignode/manager/load/LoadManager.java | 24 +++++------
 .../manager/load/balancer/RouteBalancer.java       |  8 +++-
 .../load/balancer/router/LazyGreedyRouter.java     | 26 ++++++------
 .../manager/load/heartbeat/BaseNodeCache.java      |  6 +--
 .../load/heartbeat/ConfigNodeHeartbeatCache.java   | 33 +++------------
 .../load/heartbeat/DataNodeHeartbeatCache.java     | 49 +++++++---------------
 .../load/heartbeat/NodeHeartbeatSample.java        | 29 ++++++++++++-
 .../load/balancer/router/LazyGreedyRouterTest.java |  4 +-
 .../load/balancer/router/LeaderRouterTest.java     |  8 +++-
 .../balancer/router/LoadScoreGreedyRouterTest.java |  8 +++-
 .../apache/iotdb/commons/cluster/NodeStatus.java   | 16 ++++++-
 .../java/org/apache/iotdb/db/conf/IoTDBConfig.java | 17 ++++----
 .../org/apache/iotdb/db/conf/SystemStatus.java     | 32 --------------
 .../db/conf/directories/DirectoryManager.java      |  6 +--
 .../iotdb/db/conf/directories/FolderManager.java   |  6 +--
 .../directories/strategy/DirectoryStrategy.java    |  4 +-
 .../compaction/task/CompactionRecoverTask.java     |  4 +-
 .../iotdb/db/engine/storagegroup/DataRegion.java   |  6 +--
 .../db/engine/storagegroup/TsFileProcessor.java    |  6 +--
 .../iotdb/db/metadata/logfile/MLogWriter.java      |  4 +-
 .../apache/iotdb/db/qp/executor/PlanExecutor.java  |  4 +-
 .../impl/DataNodeInternalRPCServiceImpl.java       |  1 +
 .../org/apache/iotdb/db/wal/buffer/WALBuffer.java  |  8 ++--
 .../iotdb/db/wal/checkpoint/CheckpointManager.java |  6 +--
 .../apache/iotdb/db/utils/EnvironmentUtils.java    |  4 +-
 .../apache/iotdb/spark/db/EnvironmentUtils.java    |  4 +-
 thrift/src/main/thrift/datanode.thrift             |  7 ++--
 29 files changed, 160 insertions(+), 176 deletions(-)

diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/DataNodeHeartbeatHandler.java b/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/DataNodeHeartbeatHandler.java
index 3cf5d5e419..890b448097 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/DataNodeHeartbeatHandler.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/DataNodeHeartbeatHandler.java
@@ -53,7 +53,7 @@ public class DataNodeHeartbeatHandler implements AsyncMethodCallback<THeartbeatR
 
     // Update NodeCache
     dataNodeHeartbeatCache.cacheHeartbeatSample(
-        new NodeHeartbeatSample(heartbeatResp.getHeartbeatTimestamp(), receiveTime));
+        new NodeHeartbeatSample(heartbeatResp, receiveTime));
 
     // Update RegionCache
     if (heartbeatResp.isSetJudgedLeaders()) {
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java
index 63abef6a34..38bf8f8a03 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/NodeManager.java
@@ -543,7 +543,9 @@ public class NodeManager {
    */
   private String getNodeStatus(int nodeId) {
     BaseNodeCache nodeCache = nodeCacheMap.get(nodeId);
-    return nodeCache == null ? "Unknown" : nodeCache.getNodeStatus().getStatus();
+    return nodeCache == null
+        ? NodeStatus.Unknown.getStatus()
+        : nodeCache.getNodeStatus().getStatus();
   }
 
   /**
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java
index 8ce812ade4..62a4280437 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/LoadManager.java
@@ -176,9 +176,9 @@ public class LoadManager {
   }
 
   private void updateNodeLoadStatistic() {
-    AtomicBoolean existFailDownDataNode = new AtomicBoolean(false);
-    AtomicBoolean existChangeLeaderSchemaRegionGroup = new AtomicBoolean(false);
-    AtomicBoolean existChangeLeaderDataRegionGroup = new AtomicBoolean(false);
+    AtomicBoolean existDataNodeChangesStatus = new AtomicBoolean(false);
+    AtomicBoolean existSchemaRegionGroupChangesLeader = new AtomicBoolean(false);
+    AtomicBoolean existDataRegionGroupChangesLeader = new AtomicBoolean(false);
     boolean isNeedBroadcast = false;
 
     getNodeManager()
@@ -186,10 +186,10 @@ public class LoadManager {
         .values()
         .forEach(
             nodeCache -> {
-              boolean updateResult = nodeCache.updateLoadStatistic();
+              boolean updateResult = nodeCache.updateNodeStatus();
               if (nodeCache instanceof DataNodeHeartbeatCache) {
-                // Check if some DataNodes fail down
-                existFailDownDataNode.compareAndSet(false, updateResult);
+                // Check if some DataNodes changes status
+                existDataNodeChangesStatus.compareAndSet(false, updateResult);
               }
             });
 
@@ -202,27 +202,27 @@ public class LoadManager {
               switch (regionGroupCache.getConsensusGroupId().getType()) {
                   // Check if some RegionGroups change their leader
                 case SchemaRegion:
-                  existChangeLeaderSchemaRegionGroup.compareAndSet(false, updateResult);
+                  existSchemaRegionGroupChangesLeader.compareAndSet(false, updateResult);
                   break;
                 case DataRegion:
-                  existChangeLeaderDataRegionGroup.compareAndSet(false, updateResult);
+                  existDataRegionGroupChangesLeader.compareAndSet(false, updateResult);
                   break;
               }
             });
 
-    if (existFailDownDataNode.get()) {
-      // The RegionRouteMap must be broadcast if some DataNodes fail down
+    if (existDataNodeChangesStatus.get()) {
+      // The RegionRouteMap must be broadcast if some DataNodes change status
       isNeedBroadcast = true;
     }
 
     if (RouteBalancer.LEADER_POLICY.equals(CONF.getRoutingPolicy())) {
       // Check the condition of leader routing policy
-      if (existChangeLeaderSchemaRegionGroup.get()) {
+      if (existSchemaRegionGroupChangesLeader.get()) {
         // Broadcast the RegionRouteMap if some SchemaRegionGroups change their leader
         isNeedBroadcast = true;
       }
       if (!ConsensusFactory.MultiLeaderConsensus.equals(CONF.getDataRegionConsensusProtocolClass())
-          && existChangeLeaderDataRegionGroup.get()) {
+          && existDataRegionGroupChangesLeader.get()) {
         // Broadcast the RegionRouteMap if some DataRegionGroups change their leader
         // and the consensus protocol isn't MultiLeader
         isNeedBroadcast = true;
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RouteBalancer.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RouteBalancer.java
index 48db8ee587..5dde65f600 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RouteBalancer.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/RouteBalancer.java
@@ -97,9 +97,13 @@ public class RouteBalancer {
             .getDataRegionConsensusProtocolClass()
             .equals(ConsensusFactory.MultiLeaderConsensus)) {
           // Latent router for MultiLeader consensus protocol
-          lazyGreedyRouter.updateUnknownDataNodes(
+          lazyGreedyRouter.updateDisabledDataNodes(
               getNodeManager()
-                  .filterDataNodeThroughStatus(NodeStatus.Unknown, NodeStatus.Removing));
+                  .filterDataNodeThroughStatus(
+                      NodeStatus.Unknown,
+                      NodeStatus.Removing,
+                      NodeStatus.Error,
+                      NodeStatus.ReadOnly));
           return lazyGreedyRouter;
         } else if (LEADER_POLICY.equals(policy)) {
           return new LeaderRouter(
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/router/LazyGreedyRouter.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/router/LazyGreedyRouter.java
index 549e8dc6b2..942d3fdea9 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/router/LazyGreedyRouter.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/balancer/router/LazyGreedyRouter.java
@@ -40,26 +40,26 @@ import java.util.stream.Collectors;
  */
 public class LazyGreedyRouter implements IRouter {
 
-  /** Set<DataNodeId> which stores the unknown and removing datanodes */
-  private final Set<Integer> unknownDataNodes;
+  /** Set<DataNodeId> which stores the DataNodes that unable to provide service */
+  private final Set<Integer> disabledDataNodes;
 
   private final Map<TConsensusGroupId, TRegionReplicaSet> routeMap;
 
   public LazyGreedyRouter() {
-    this.unknownDataNodes = Collections.synchronizedSet(new HashSet<>());
+    this.disabledDataNodes = Collections.synchronizedSet(new HashSet<>());
     this.routeMap = new ConcurrentHashMap<>();
   }
 
   /**
-   * Update unknownDataNodes cache in LazyRandomRouter
+   * Update the disabledDataNodes cache in LazyRandomRouter
    *
-   * @param unknownDataNodes DataNodes that have unknown status
+   * @param disabledDataNodes DataNodes whose status aren't Running
    */
-  public void updateUnknownDataNodes(List<TDataNodeConfiguration> unknownDataNodes) {
-    synchronized (this.unknownDataNodes) {
-      this.unknownDataNodes.clear();
-      this.unknownDataNodes.addAll(
-          unknownDataNodes.stream()
+  public void updateDisabledDataNodes(List<TDataNodeConfiguration> disabledDataNodes) {
+    synchronized (this.disabledDataNodes) {
+      this.disabledDataNodes.clear();
+      this.disabledDataNodes.addAll(
+          disabledDataNodes.stream()
               .map(dataNodeConfiguration -> dataNodeConfiguration.getLocation().getDataNodeId())
               .collect(Collectors.toList()));
     }
@@ -68,7 +68,7 @@ public class LazyGreedyRouter implements IRouter {
   @Override
   public Map<TConsensusGroupId, TRegionReplicaSet> genLatestRegionRouteMap(
       List<TRegionReplicaSet> replicaSets) {
-    synchronized (unknownDataNodes) {
+    synchronized (disabledDataNodes) {
       // Map<DataNodeId, leaderCount> Count the number of leaders in each DataNodes
       Map<Integer, Integer> leaderCounter = new HashMap<>();
       Map<TConsensusGroupId, TRegionReplicaSet> result = new ConcurrentHashMap<>();
@@ -120,7 +120,7 @@ public class LazyGreedyRouter implements IRouter {
 
     // The RouteEntry needs update when the status of DataNode corresponding to the first priority
     // is unknown
-    return unknownDataNodes.contains(
+    return disabledDataNodes.contains(
         routeMap.get(groupId).getDataNodeLocations().get(0).getDataNodeId());
   }
 
@@ -133,7 +133,7 @@ public class LazyGreedyRouter implements IRouter {
     int locateLeaderCount = Integer.MAX_VALUE;
     for (int i = 0; i < newRouteEntry.getDataNodeLocationsSize(); i++) {
       int currentDataNodeId = newRouteEntry.getDataNodeLocations().get(i).getDataNodeId();
-      if (!unknownDataNodes.contains(currentDataNodeId)
+      if (!disabledDataNodes.contains(currentDataNodeId)
           && leaderCounter.getOrDefault(currentDataNodeId, 0) < locateLeaderCount) {
         leaderIndex = i;
         locateLeaderCount = leaderCounter.getOrDefault(currentDataNodeId, 0);
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/BaseNodeCache.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/BaseNodeCache.java
index c78392544c..6bb0cb25b5 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/BaseNodeCache.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/BaseNodeCache.java
@@ -48,11 +48,11 @@ public abstract class BaseNodeCache {
   public abstract void cacheHeartbeatSample(NodeHeartbeatSample newHeartbeatSample);
 
   /**
-   * Invoking periodically to update Nodes' load statistics
+   * Invoking periodically to update Nodes' current running status
    *
-   * @return true if some load statistic changed
+   * @return True if the specific Node's status changed, false otherwise
    */
-  public abstract boolean updateLoadStatistic();
+  public abstract boolean updateNodeStatus();
 
   /**
    * TODO: The loadScore of each Node will be changed to Double
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/ConfigNodeHeartbeatCache.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/ConfigNodeHeartbeatCache.java
index 00e4057f4b..09d07aa29d 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/ConfigNodeHeartbeatCache.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/ConfigNodeHeartbeatCache.java
@@ -47,7 +47,7 @@ public class ConfigNodeHeartbeatCache extends BaseNodeCache {
   }
 
   @Override
-  public boolean updateLoadStatistic() {
+  public boolean updateNodeStatus() {
     if (configNodeLocation.getInternalEndPoint().equals(NodeManager.CURRENT_NODE)) {
       this.status = NodeStatus.Running;
       return false;
@@ -60,15 +60,7 @@ public class ConfigNodeHeartbeatCache extends BaseNodeCache {
       }
     }
 
-    NodeStatus originStatus;
-    switch (status) {
-      case Running:
-        originStatus = NodeStatus.Running;
-        break;
-      case Unknown:
-      default:
-        originStatus = NodeStatus.Unknown;
-    }
+    String originStatus = status.getStatus();
 
     // TODO: Optimize judge logic
     if (System.currentTimeMillis() - lastSendTime > HEARTBEAT_TIMEOUT_TIME) {
@@ -76,31 +68,18 @@ public class ConfigNodeHeartbeatCache extends BaseNodeCache {
     } else {
       status = NodeStatus.Running;
     }
-    return !status.getStatus().equals(originStatus.getStatus());
+    return !status.getStatus().equals(originStatus);
   }
 
   @Override
   public long getLoadScore() {
-    // Return a copy of loadScore
-    switch (status) {
-      case Running:
-        return 0;
-      case Unknown:
-      default:
-        // The Unknown Node will get the highest loadScore
-        return Long.MAX_VALUE;
-    }
+    // The ConfigNode whose status isn't Running will get the highest loadScore
+    return status == NodeStatus.Running ? 0 : Long.MAX_VALUE;
   }
 
   @Override
   public NodeStatus getNodeStatus() {
     // Return a copy of status
-    switch (status) {
-      case Running:
-        return NodeStatus.Running;
-      case Unknown:
-      default:
-        return NodeStatus.Unknown;
-    }
+    return NodeStatus.valueOf(status.getStatus());
   }
 }
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/DataNodeHeartbeatCache.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/DataNodeHeartbeatCache.java
index ba56d867e7..bf4f15a4b7 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/DataNodeHeartbeatCache.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/DataNodeHeartbeatCache.java
@@ -51,41 +51,33 @@ public class DataNodeHeartbeatCache extends BaseNodeCache {
   }
 
   @Override
-  public boolean updateLoadStatistic() {
+  public boolean updateNodeStatus() {
     if (isRemoving()) {
       return false;
     }
 
-    long lastSendTime = 0;
+    NodeHeartbeatSample lastSample = null;
     synchronized (slidingWindow) {
       if (slidingWindow.size() > 0) {
-        lastSendTime = slidingWindow.getLast().getSendTimestamp();
+        lastSample = slidingWindow.getLast();
       }
     }
+    long lastSendTime = lastSample == null ? 0 : lastSample.getSendTimestamp();
 
     /* Update loadScore */
-    if (lastSendTime > 0) {
-      loadScore = -lastSendTime;
-    }
+    loadScore = -lastSendTime;
 
     /* Update Node status */
-    NodeStatus originStatus;
-    switch (status) {
-      case Running:
-        originStatus = NodeStatus.Running;
-        break;
-      case Unknown:
-      default:
-        originStatus = NodeStatus.Unknown;
-    }
-
+    String originStatus = status.getStatus();
     // TODO: Optimize judge logic
     if (System.currentTimeMillis() - lastSendTime > HEARTBEAT_TIMEOUT_TIME) {
       status = NodeStatus.Unknown;
-    } else {
-      status = NodeStatus.Running;
+    } else if (lastSample != null) {
+      status = lastSample.getStatus();
     }
-    return !status.getStatus().equals(originStatus.getStatus());
+
+    return NodeStatus.isNormalStatus(status)
+        != NodeStatus.isNormalStatus(NodeStatus.valueOf(originStatus));
   }
 
   @Override
@@ -94,15 +86,8 @@ public class DataNodeHeartbeatCache extends BaseNodeCache {
       return Long.MAX_VALUE;
     }
 
-    // Return a copy of loadScore
-    switch (status) {
-      case Running:
-        return loadScore;
-      case Unknown:
-      default:
-        // The Unknown Node will get the highest loadScore
-        return Long.MAX_VALUE;
-    }
+    // The DataNode whose status isn't Running will get the highest loadScore
+    return status == NodeStatus.Running ? loadScore : Long.MAX_VALUE;
   }
 
   @Override
@@ -112,12 +97,6 @@ public class DataNodeHeartbeatCache extends BaseNodeCache {
     }
 
     // Return a copy of status
-    switch (status) {
-      case Running:
-        return NodeStatus.Running;
-      case Unknown:
-      default:
-        return NodeStatus.Unknown;
-    }
+    return NodeStatus.valueOf(status.getStatus());
   }
 }
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/NodeHeartbeatSample.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/NodeHeartbeatSample.java
index 509642c2fc..507b420c0a 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/NodeHeartbeatSample.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/load/heartbeat/NodeHeartbeatSample.java
@@ -18,19 +18,34 @@
  */
 package org.apache.iotdb.confignode.manager.load.heartbeat;
 
+import org.apache.iotdb.commons.cluster.NodeStatus;
+import org.apache.iotdb.mpp.rpc.thrift.THeartbeatResp;
+
 public class NodeHeartbeatSample {
 
   // Unit: ms
   private final long sendTimestamp;
   private final long receiveTimestamp;
 
-  // TODO: Add load sample
+  private NodeStatus status;
+  private short cpu;
+  private short memory;
 
+  /** Constructor for ConfigNode sample */
   public NodeHeartbeatSample(long sendTimestamp, long receiveTimestamp) {
     this.sendTimestamp = sendTimestamp;
     this.receiveTimestamp = receiveTimestamp;
   }
 
+  /** Constructor for DataNode sample */
+  public NodeHeartbeatSample(THeartbeatResp heartbeatResp, long receiveTimestamp) {
+    this.sendTimestamp = heartbeatResp.getHeartbeatTimestamp();
+    this.receiveTimestamp = receiveTimestamp;
+    this.status = NodeStatus.valueOf(heartbeatResp.getStatus());
+    this.cpu = heartbeatResp.getCpu();
+    this.memory = heartbeatResp.getMemory();
+  }
+
   public long getSendTimestamp() {
     return sendTimestamp;
   }
@@ -38,4 +53,16 @@ public class NodeHeartbeatSample {
   public long getReceiveTimestamp() {
     return receiveTimestamp;
   }
+
+  public NodeStatus getStatus() {
+    return status;
+  }
+
+  public short getCpu() {
+    return cpu;
+  }
+
+  public short getMemory() {
+    return memory;
+  }
 }
diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/balancer/router/LazyGreedyRouterTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/balancer/router/LazyGreedyRouterTest.java
index b3adbec0dc..533e7e3870 100644
--- a/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/balancer/router/LazyGreedyRouterTest.java
+++ b/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/balancer/router/LazyGreedyRouterTest.java
@@ -75,7 +75,7 @@ public class LazyGreedyRouterTest {
         new TDataNodeConfiguration().setLocation(new TDataNodeLocation().setDataNodeId(2)));
 
     /* Test2: The number of leaders in DataNode-1 and DataNode-3 should be approximately 6 */
-    lazyGreedyRouter.updateUnknownDataNodes(dataNodeConfigurations);
+    lazyGreedyRouter.updateDisabledDataNodes(dataNodeConfigurations);
     leaderCounter.clear();
     routeMap = lazyGreedyRouter.genLatestRegionRouteMap(regionReplicaSetList);
     routeMap
@@ -145,7 +145,7 @@ public class LazyGreedyRouterTest {
         new TDataNodeConfiguration().setLocation(new TDataNodeLocation().setDataNodeId(2)));
 
     /* Test2: The number of leaders in DataNode-1 and DataNode-3 should be exactly 9 */
-    lazyGreedyRouter.updateUnknownDataNodes(dataNodeConfigurations);
+    lazyGreedyRouter.updateDisabledDataNodes(dataNodeConfigurations);
     leaderCounter.clear();
     routeMap = lazyGreedyRouter.genLatestRegionRouteMap(regionReplicaSetList);
     routeMap
diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/balancer/router/LeaderRouterTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/balancer/router/LeaderRouterTest.java
index de0fb369c5..de501034ec 100644
--- a/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/balancer/router/LeaderRouterTest.java
+++ b/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/balancer/router/LeaderRouterTest.java
@@ -23,12 +23,14 @@ 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.commons.cluster.NodeStatus;
 import org.apache.iotdb.confignode.manager.load.heartbeat.BaseNodeCache;
 import org.apache.iotdb.confignode.manager.load.heartbeat.DataNodeHeartbeatCache;
 import org.apache.iotdb.confignode.manager.load.heartbeat.IRegionGroupCache;
 import org.apache.iotdb.confignode.manager.load.heartbeat.NodeHeartbeatSample;
 import org.apache.iotdb.confignode.manager.load.heartbeat.RegionGroupCache;
 import org.apache.iotdb.confignode.manager.load.heartbeat.RegionHeartbeatSample;
+import org.apache.iotdb.mpp.rpc.thrift.THeartbeatResp;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -66,9 +68,11 @@ public class LeaderRouterTest {
       nodeCacheMap
           .get(i)
           .cacheHeartbeatSample(
-              new NodeHeartbeatSample(currentTimeMillis - i * 1000, currentTimeMillis - i * 1000));
+              new NodeHeartbeatSample(
+                  new THeartbeatResp(currentTimeMillis - i * 1000, NodeStatus.Running.getStatus()),
+                  currentTimeMillis - i * 1000));
     }
-    nodeCacheMap.values().forEach(BaseNodeCache::updateLoadStatistic);
+    nodeCacheMap.values().forEach(BaseNodeCache::updateNodeStatus);
 
     // Get the loadScoreMap
     Map<Integer, Long> loadScoreMap = new ConcurrentHashMap<>();
diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/balancer/router/LoadScoreGreedyRouterTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/balancer/router/LoadScoreGreedyRouterTest.java
index ae13d16650..8d4d36d794 100644
--- a/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/balancer/router/LoadScoreGreedyRouterTest.java
+++ b/confignode/src/test/java/org/apache/iotdb/confignode/manager/load/balancer/router/LoadScoreGreedyRouterTest.java
@@ -23,9 +23,11 @@ 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.commons.cluster.NodeStatus;
 import org.apache.iotdb.confignode.manager.load.heartbeat.BaseNodeCache;
 import org.apache.iotdb.confignode.manager.load.heartbeat.DataNodeHeartbeatCache;
 import org.apache.iotdb.confignode.manager.load.heartbeat.NodeHeartbeatSample;
+import org.apache.iotdb.mpp.rpc.thrift.THeartbeatResp;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -63,9 +65,11 @@ public class LoadScoreGreedyRouterTest {
       nodeCacheMap
           .get(i)
           .cacheHeartbeatSample(
-              new NodeHeartbeatSample(currentTimeMillis - i * 1000, currentTimeMillis - i * 1000));
+              new NodeHeartbeatSample(
+                  new THeartbeatResp(currentTimeMillis - i * 1000, NodeStatus.Running.getStatus()),
+                  currentTimeMillis - i * 1000));
     }
-    nodeCacheMap.values().forEach(BaseNodeCache::updateLoadStatistic);
+    nodeCacheMap.values().forEach(BaseNodeCache::updateNodeStatus);
 
     /* Get the loadScoreMap */
     Map<Integer, Long> loadScoreMap = new ConcurrentHashMap<>();
diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/cluster/NodeStatus.java b/node-commons/src/main/java/org/apache/iotdb/commons/cluster/NodeStatus.java
index 82cc285cf3..d99db6f40f 100644
--- a/node-commons/src/main/java/org/apache/iotdb/commons/cluster/NodeStatus.java
+++ b/node-commons/src/main/java/org/apache/iotdb/commons/cluster/NodeStatus.java
@@ -27,7 +27,16 @@ public enum NodeStatus {
   Unknown("Unknown"),
 
   /** Node is in removing */
-  Removing("Removing");
+  Removing("Removing"),
+
+  /** Only query statements are permitted */
+  ReadOnly("Read-Only"),
+
+  /**
+   * Unrecoverable errors occur, system will be read-only or exit according to the param
+   * allow_read_only_when_errors_occur
+   */
+  Error("Error");
 
   private final String status;
 
@@ -38,4 +47,9 @@ public enum NodeStatus {
   public String getStatus() {
     return status;
   }
+
+  public static boolean isNormalStatus(NodeStatus status) {
+    // Currently, the only normal status is Running
+    return status.equals(NodeStatus.Running);
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
index 302fcebaf4..f46de58663 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
@@ -19,6 +19,7 @@
 package org.apache.iotdb.db.conf;
 
 import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.consensus.ConsensusFactory;
 import org.apache.iotdb.db.conf.directories.DirectoryManager;
@@ -87,7 +88,7 @@ public class IoTDBConfig {
   private boolean allowReadOnlyWhenErrorsOccur = true;
 
   /** Status of current system. */
-  private volatile SystemStatus status = SystemStatus.NORMAL;
+  private volatile NodeStatus status = NodeStatus.Running;
 
   /** whether to enable the mqtt service. */
   private boolean enableMQTTService = false;
@@ -1554,25 +1555,25 @@ public class IoTDBConfig {
   }
 
   public boolean isReadOnly() {
-    return status == SystemStatus.READ_ONLY
-        || (status == SystemStatus.ERROR && allowReadOnlyWhenErrorsOccur);
+    return status == NodeStatus.ReadOnly
+        || (status == NodeStatus.Error && allowReadOnlyWhenErrorsOccur);
   }
 
-  public SystemStatus getSystemStatus() {
+  public NodeStatus getNodeStatus() {
     return status;
   }
 
-  public void setSystemStatus(SystemStatus newStatus) {
-    if (newStatus == SystemStatus.READ_ONLY) {
+  public void setNodeStatus(NodeStatus newStatus) {
+    if (newStatus == NodeStatus.ReadOnly) {
       logger.error(
           "Change system mode to read-only! Only query statements are permitted!",
           new RuntimeException("System mode is set to READ_ONLY"));
-    } else if (newStatus == SystemStatus.ERROR) {
+    } else if (newStatus == NodeStatus.Error) {
       if (allowReadOnlyWhenErrorsOccur) {
         logger.error(
             "Unrecoverable error occurs! Make system read-only when allow_read_only_when_errors_occur is true.",
             new RuntimeException("System mode is set to READ_ONLY"));
-        newStatus = SystemStatus.READ_ONLY;
+        newStatus = NodeStatus.ReadOnly;
       } else {
         logger.error(
             "Unrecoverable error occurs! Shutdown system directly when allow_read_only_when_errors_occur is false.",
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/SystemStatus.java b/server/src/main/java/org/apache/iotdb/db/conf/SystemStatus.java
deleted file mode 100644
index 745fbb1529..0000000000
--- a/server/src/main/java/org/apache/iotdb/db/conf/SystemStatus.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.db.conf;
-
-/** Status of current system */
-public enum SystemStatus {
-  /** System can read and write normally */
-  NORMAL,
-  /** Only query statements are permitted */
-  READ_ONLY,
-  /**
-   * Unrecoverable errors occur, system will be read-only or exit according to the param
-   * allow_read_only_when_errors_occur
-   */
-  ERROR,
-}
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/directories/DirectoryManager.java b/server/src/main/java/org/apache/iotdb/db/conf/directories/DirectoryManager.java
index 5233337cf0..45855f6f50 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/directories/DirectoryManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/directories/DirectoryManager.java
@@ -18,10 +18,10 @@
  */
 package org.apache.iotdb.db.conf.directories;
 
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.commons.utils.TestOnly;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.conf.SystemStatus;
 import org.apache.iotdb.db.conf.directories.strategy.DirectoryStrategy;
 import org.apache.iotdb.db.exception.DiskSpaceInsufficientException;
 import org.apache.iotdb.db.exception.LoadConfigurationException;
@@ -147,7 +147,7 @@ public class DirectoryManager {
       return sequenceFileFolders.get(sequenceStrategy.nextFolderIndex());
     } catch (DiskSpaceInsufficientException e) {
       logger.error("All disks of wal folders are full, change system mode to read-only.", e);
-      IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.READ_ONLY);
+      IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.ReadOnly);
       throw e;
     }
   }
@@ -161,7 +161,7 @@ public class DirectoryManager {
       return unsequenceFileFolders.get(unsequenceStrategy.nextFolderIndex());
     } catch (DiskSpaceInsufficientException e) {
       logger.error("All disks of wal folders are full, change system mode to read-only.", e);
-      IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.READ_ONLY);
+      IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.ReadOnly);
       throw e;
     }
   }
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/directories/FolderManager.java b/server/src/main/java/org/apache/iotdb/db/conf/directories/FolderManager.java
index 23e61a2663..a6b589534b 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/directories/FolderManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/directories/FolderManager.java
@@ -18,8 +18,8 @@
  */
 package org.apache.iotdb.db.conf.directories;
 
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.conf.SystemStatus;
 import org.apache.iotdb.db.conf.directories.strategy.DirectoryStrategy;
 import org.apache.iotdb.db.conf.directories.strategy.DirectoryStrategyType;
 import org.apache.iotdb.db.conf.directories.strategy.MaxDiskUsableSpaceFirstStrategy;
@@ -62,7 +62,7 @@ public class FolderManager {
       this.selectStrategy.setFolders(folders);
     } catch (DiskSpaceInsufficientException e) {
       logger.error("All disks of wal folders are full, change system mode to read-only.", e);
-      IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.READ_ONLY);
+      IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.ReadOnly);
       throw e;
     }
   }
@@ -72,7 +72,7 @@ public class FolderManager {
       return folders.get(selectStrategy.nextFolderIndex());
     } catch (DiskSpaceInsufficientException e) {
       logger.error("All disks of wal folders are full, change system mode to read-only.", e);
-      IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.READ_ONLY);
+      IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.ReadOnly);
       throw e;
     }
   }
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/directories/strategy/DirectoryStrategy.java b/server/src/main/java/org/apache/iotdb/db/conf/directories/strategy/DirectoryStrategy.java
index 2758231515..8fdfc8ca29 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/directories/strategy/DirectoryStrategy.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/directories/strategy/DirectoryStrategy.java
@@ -18,9 +18,9 @@
  */
 package org.apache.iotdb.db.conf.directories.strategy;
 
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.commons.utils.JVMCommonUtils;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.conf.SystemStatus;
 import org.apache.iotdb.db.exception.DiskSpaceInsufficientException;
 
 import org.slf4j.Logger;
@@ -56,7 +56,7 @@ public abstract class DirectoryStrategy {
     }
     if (!hasSpace) {
       logger.error("Disk space is insufficient, change system mode to read-only");
-      IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.READ_ONLY);
+      IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.ReadOnly);
       throw new DiskSpaceInsufficientException(folders);
     }
 
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/compaction/task/CompactionRecoverTask.java b/server/src/main/java/org/apache/iotdb/db/engine/compaction/task/CompactionRecoverTask.java
index 9d8807e897..4740e5b298 100644
--- a/server/src/main/java/org/apache/iotdb/db/engine/compaction/task/CompactionRecoverTask.java
+++ b/server/src/main/java/org/apache/iotdb/db/engine/compaction/task/CompactionRecoverTask.java
@@ -18,9 +18,9 @@
  */
 package org.apache.iotdb.db.engine.compaction.task;
 
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.conf.SystemStatus;
 import org.apache.iotdb.db.engine.compaction.CompactionUtils;
 import org.apache.iotdb.db.engine.compaction.log.CompactionLogAnalyzer;
 import org.apache.iotdb.db.engine.compaction.log.CompactionLogger;
@@ -302,7 +302,7 @@ public class CompactionRecoverTask {
             "{} [Compaction][ExceptionHandler] target file {} is not complete, and some source files is lost, do nothing. Set allowCompaction to false",
             fullStorageGroupName,
             targetFileIdentifier.getFilePath());
-        IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.READ_ONLY);
+        IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.ReadOnly);
         return false;
       }
     }
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java
index 493da27014..4c7289f494 100755
--- a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java
+++ b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/DataRegion.java
@@ -19,6 +19,7 @@
 package org.apache.iotdb.db.engine.storagegroup;
 
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory;
 import org.apache.iotdb.commons.concurrent.ThreadName;
 import org.apache.iotdb.commons.concurrent.threadpool.ScheduledExecutorUtil;
@@ -31,7 +32,6 @@ import org.apache.iotdb.commons.utils.TestOnly;
 import org.apache.iotdb.consensus.ConsensusFactory;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.conf.SystemStatus;
 import org.apache.iotdb.db.conf.directories.DirectoryManager;
 import org.apache.iotdb.db.engine.StorageEngine;
 import org.apache.iotdb.db.engine.StorageEngineV2;
@@ -1479,7 +1479,7 @@ public class DataRegion {
         logger.error(
             "disk space is insufficient when creating TsFile processor, change system mode to read-only",
             e);
-        IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.READ_ONLY);
+        IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.ReadOnly);
         break;
       } catch (IOException e) {
         if (retryCnt < 3) {
@@ -1488,7 +1488,7 @@ public class DataRegion {
         } else {
           logger.error(
               "meet IOException when creating TsFileProcessor, change system mode to error", e);
-          IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.ERROR);
+          IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.Error);
           break;
         }
       }
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessor.java b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessor.java
index 35e022fd96..b8964b517a 100644
--- a/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessor.java
+++ b/server/src/main/java/org/apache/iotdb/db/engine/storagegroup/TsFileProcessor.java
@@ -19,13 +19,13 @@
 package org.apache.iotdb.db.engine.storagegroup;
 
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.commons.exception.IllegalPathException;
 import org.apache.iotdb.commons.exception.MetadataException;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.commons.utils.TestOnly;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.conf.SystemStatus;
 import org.apache.iotdb.db.conf.adapter.CompressionRatio;
 import org.apache.iotdb.db.engine.StorageEngine;
 import org.apache.iotdb.db.engine.flush.CloseFileListener;
@@ -1238,7 +1238,7 @@ public class TsFileProcessor {
               storageGroupName,
               tsFileResource.getTsFile().getName(),
               e);
-          IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.ERROR);
+          IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.Error);
           try {
             logger.error(
                 "{}: {} IOTask meets error, truncate the corrupted data",
@@ -1368,7 +1368,7 @@ public class TsFileProcessor {
               storageGroupName,
               tsFileResource.getTsFile().getAbsolutePath(),
               e);
-          IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.ERROR);
+          IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.Error);
           break;
         }
       }
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/logfile/MLogWriter.java b/server/src/main/java/org/apache/iotdb/db/metadata/logfile/MLogWriter.java
index 86d64ddf03..b11db2dc3e 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/logfile/MLogWriter.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/logfile/MLogWriter.java
@@ -18,11 +18,11 @@
  */
 package org.apache.iotdb.db.metadata.logfile;
 
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.commons.file.SystemFileFactory;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.conf.SystemStatus;
 import org.apache.iotdb.db.metadata.mnode.IMNode;
 import org.apache.iotdb.db.metadata.mnode.IMeasurementMNode;
 import org.apache.iotdb.db.metadata.mnode.IStorageGroupMNode;
@@ -124,7 +124,7 @@ public class MLogWriter implements AutoCloseable {
         } else {
           logger.error(
               "MLog {} sync failed, change system mode to error", logFile.getAbsoluteFile(), e);
-          IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.ERROR);
+          IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.Error);
           break;
         }
       }
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java b/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
index 96bdb1bc88..5ff9feb5f6 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
@@ -25,6 +25,7 @@ import org.apache.iotdb.commons.auth.entity.PathPrivilege;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
 import org.apache.iotdb.commons.auth.entity.Role;
 import org.apache.iotdb.commons.auth.entity.User;
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory;
 import org.apache.iotdb.commons.concurrent.ThreadName;
 import org.apache.iotdb.commons.conf.CommonDescriptor;
@@ -39,7 +40,6 @@ import org.apache.iotdb.commons.utils.AuthUtils;
 import org.apache.iotdb.db.auth.AuthorityChecker;
 import org.apache.iotdb.db.auth.AuthorizerManager;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.conf.SystemStatus;
 import org.apache.iotdb.db.engine.StorageEngine;
 import org.apache.iotdb.db.engine.cache.BloomFilterCache;
 import org.apache.iotdb.db.engine.cache.ChunkCache;
@@ -579,7 +579,7 @@ public class PlanExecutor implements IPlanExecutor {
   private void operateSetSystemMode(SetSystemModePlan plan) {
     IoTDBDescriptor.getInstance()
         .getConfig()
-        .setSystemStatus(plan.isReadOnly() ? SystemStatus.READ_ONLY : SystemStatus.NORMAL);
+        .setNodeStatus(plan.isReadOnly() ? NodeStatus.ReadOnly : NodeStatus.Running);
   }
 
   private void operateFlush(FlushPlan plan) throws StorageGroupNotSetException {
diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeInternalRPCServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeInternalRPCServiceImpl.java
index 046c3b1967..9bcec6f348 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeInternalRPCServiceImpl.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeInternalRPCServiceImpl.java
@@ -384,6 +384,7 @@ public class DataNodeInternalRPCServiceImpl implements IDataNodeRPCService.Iface
   public THeartbeatResp getDataNodeHeartBeat(THeartbeatReq req) throws TException {
     THeartbeatResp resp = new THeartbeatResp();
     resp.setHeartbeatTimestamp(req.getHeartbeatTimestamp());
+    resp.setStatus(IoTDBDescriptor.getInstance().getConfig().getNodeStatus().getStatus());
 
     // Judging leader if necessary
     if (req.isNeedJudgeLeader()) {
diff --git a/server/src/main/java/org/apache/iotdb/db/wal/buffer/WALBuffer.java b/server/src/main/java/org/apache/iotdb/db/wal/buffer/WALBuffer.java
index 820766a468..1dc2f4f541 100644
--- a/server/src/main/java/org/apache/iotdb/db/wal/buffer/WALBuffer.java
+++ b/server/src/main/java/org/apache/iotdb/db/wal/buffer/WALBuffer.java
@@ -18,11 +18,11 @@
  */
 package org.apache.iotdb.db.wal.buffer;
 
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory;
 import org.apache.iotdb.commons.concurrent.ThreadName;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.conf.SystemStatus;
 import org.apache.iotdb.db.mpp.plan.planner.plan.node.write.InsertNode;
 import org.apache.iotdb.db.utils.MmapUtil;
 import org.apache.iotdb.db.wal.exception.WALNodeClosedException;
@@ -420,7 +420,7 @@ public class WALBuffer extends AbstractWALBuffer {
       } catch (Throwable e) {
         logger.error(
             "Fail to sync wal node-{}'s buffer, change system mode to error.", identifier, e);
-        config.setSystemStatus(SystemStatus.ERROR);
+        config.setNodeStatus(NodeStatus.Error);
       } finally {
         switchSyncingBufferToIdle();
       }
@@ -441,7 +441,7 @@ public class WALBuffer extends AbstractWALBuffer {
           if (info.rollWALFileWriterListener != null) {
             info.rollWALFileWriterListener.fail(e);
           }
-          config.setSystemStatus(SystemStatus.ERROR);
+          config.setNodeStatus(NodeStatus.Error);
         }
       } else if (forceFlag) { // force os cache to the storage device, avoid force twice by judging
         // after rolling file
@@ -456,7 +456,7 @@ public class WALBuffer extends AbstractWALBuffer {
           for (WALFlushListener fsyncListener : info.fsyncListeners) {
             fsyncListener.fail(e);
           }
-          config.setSystemStatus(SystemStatus.ERROR);
+          config.setNodeStatus(NodeStatus.Error);
         }
       }
 
diff --git a/server/src/main/java/org/apache/iotdb/db/wal/checkpoint/CheckpointManager.java b/server/src/main/java/org/apache/iotdb/db/wal/checkpoint/CheckpointManager.java
index 5d7bb51a42..b71060c5e2 100644
--- a/server/src/main/java/org/apache/iotdb/db/wal/checkpoint/CheckpointManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/wal/checkpoint/CheckpointManager.java
@@ -18,10 +18,10 @@
  */
 package org.apache.iotdb.db.wal.checkpoint;
 
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.commons.file.SystemFileFactory;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.conf.SystemStatus;
 import org.apache.iotdb.db.wal.io.CheckpointWriter;
 import org.apache.iotdb.db.wal.io.ILogWriter;
 import org.apache.iotdb.db.wal.utils.CheckpointFileUtils;
@@ -174,7 +174,7 @@ public class CheckpointManager implements AutoCloseable {
             "Fail to fsync wal node-{}'s checkpoint writer, change system mode to error.",
             identifier,
             e);
-        config.setSystemStatus(SystemStatus.ERROR);
+        config.setNodeStatus(NodeStatus.Error);
       }
 
       try {
@@ -193,7 +193,7 @@ public class CheckpointManager implements AutoCloseable {
             "Fail to roll wal node-{}'s checkpoint writer, change system mode to error.",
             identifier,
             e);
-        config.setSystemStatus(SystemStatus.ERROR);
+        config.setNodeStatus(NodeStatus.Error);
       }
     } finally {
       infoLock.unlock();
diff --git a/server/src/test/java/org/apache/iotdb/db/utils/EnvironmentUtils.java b/server/src/test/java/org/apache/iotdb/db/utils/EnvironmentUtils.java
index df70dcd56b..1158750b02 100644
--- a/server/src/test/java/org/apache/iotdb/db/utils/EnvironmentUtils.java
+++ b/server/src/test/java/org/apache/iotdb/db/utils/EnvironmentUtils.java
@@ -19,13 +19,13 @@
 package org.apache.iotdb.db.utils;
 
 import org.apache.iotdb.commons.auth.AuthException;
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.commons.conf.CommonConfig;
 import org.apache.iotdb.commons.conf.CommonDescriptor;
 import org.apache.iotdb.commons.udf.service.UDFRegistrationService;
 import org.apache.iotdb.db.auth.AuthorizerManager;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.conf.SystemStatus;
 import org.apache.iotdb.db.conf.directories.DirectoryManager;
 import org.apache.iotdb.db.constant.TestConstant;
 import org.apache.iotdb.db.engine.StorageEngine;
@@ -158,7 +158,7 @@ public class EnvironmentUtils {
       fail();
     }
 
-    IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.NORMAL);
+    IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.Running);
     // We must disable MQTT service as it will cost a lot of time to be shutdown, which may slow our
     // unit tests.
     IoTDBDescriptor.getInstance().getConfig().setEnableMQTTService(false);
diff --git a/spark-iotdb-connector/src/test/scala/org/apache/iotdb/spark/db/EnvironmentUtils.java b/spark-iotdb-connector/src/test/scala/org/apache/iotdb/spark/db/EnvironmentUtils.java
index 50fafca009..b137f8140e 100644
--- a/spark-iotdb-connector/src/test/scala/org/apache/iotdb/spark/db/EnvironmentUtils.java
+++ b/spark-iotdb-connector/src/test/scala/org/apache/iotdb/spark/db/EnvironmentUtils.java
@@ -29,9 +29,9 @@ import org.apache.commons.io.FileUtils;
 import org.apache.iotdb.commons.auth.AuthException;
 import org.apache.iotdb.commons.auth.authorizer.BasicAuthorizer;
 import org.apache.iotdb.commons.auth.authorizer.IAuthorizer;
+import org.apache.iotdb.commons.cluster.NodeStatus;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.conf.SystemStatus;
 import org.apache.iotdb.db.conf.directories.DirectoryManager;
 import org.apache.iotdb.db.engine.StorageEngine;
 import org.apache.iotdb.db.engine.cache.BloomFilterCache;
@@ -112,7 +112,7 @@ public class EnvironmentUtils {
       Assert.fail();
     }
     StorageEngine.getInstance().reset();
-    IoTDBDescriptor.getInstance().getConfig().setSystemStatus(SystemStatus.NORMAL);
+    IoTDBDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.Running);
 
     // clean wal
     WALManager.getInstance().stop();
diff --git a/thrift/src/main/thrift/datanode.thrift b/thrift/src/main/thrift/datanode.thrift
index dc26980368..202a0948c1 100644
--- a/thrift/src/main/thrift/datanode.thrift
+++ b/thrift/src/main/thrift/datanode.thrift
@@ -184,9 +184,10 @@ struct THeartbeatReq {
 
 struct THeartbeatResp {
   1: required i64 heartbeatTimestamp
-  2: optional map<common.TConsensusGroupId, bool> judgedLeaders
-  3: optional i16 cpu
-  4: optional i16 memory
+  2: required string status
+  3: optional map<common.TConsensusGroupId, bool> judgedLeaders
+  4: optional i16 cpu
+  5: optional i16 memory
 }
 
 struct TRegionRouteReq {