[To rel/1.2]Enhance versionInfo in show cluster and show cluster detail (#10744)

diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java
index ffd7fc4..b097bdb 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java
@@ -53,7 +53,7 @@
 import org.apache.iotdb.confignode.consensus.request.read.trigger.GetTriggerTablePlan;
 import org.apache.iotdb.confignode.consensus.request.write.confignode.ApplyConfigNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.confignode.RemoveConfigNodePlan;
-import org.apache.iotdb.confignode.consensus.request.write.confignode.UpdateBuildInfoPlan;
+import org.apache.iotdb.confignode.consensus.request.write.confignode.UpdateVersionInfoPlan;
 import org.apache.iotdb.confignode.consensus.request.write.cq.ActiveCQPlan;
 import org.apache.iotdb.confignode.consensus.request.write.cq.AddCQPlan;
 import org.apache.iotdb.confignode.consensus.request.write.cq.DropCQPlan;
@@ -281,8 +281,8 @@
         case RemoveConfigNode:
           plan = new RemoveConfigNodePlan();
           break;
-        case UpdateBuildInfo:
-          plan = new UpdateBuildInfoPlan();
+        case UpdateVersionInfo:
+          plan = new UpdateVersionInfoPlan();
           break;
         case CreateFunction:
           plan = new CreateFunctionPlan();
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java
index 8644823..3392b28 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java
@@ -27,7 +27,7 @@
   /** ConfigNode. */
   ApplyConfigNode((short) 0),
   RemoveConfigNode((short) 1),
-  UpdateBuildInfo((short) 2),
+  UpdateVersionInfo((short) 2),
 
   /** DataNode. */
   RegisterDataNode((short) 100),
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/confignode/UpdateBuildInfoPlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/confignode/UpdateVersionInfoPlan.java
similarity index 66%
rename from iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/confignode/UpdateBuildInfoPlan.java
rename to iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/confignode/UpdateVersionInfoPlan.java
index 8fdee39..3a562f1 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/confignode/UpdateBuildInfoPlan.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/confignode/UpdateVersionInfoPlan.java
@@ -21,6 +21,7 @@
 
 import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan;
 import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType;
+import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
 import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
 
 import java.io.DataOutputStream;
@@ -28,23 +29,23 @@
 import java.nio.ByteBuffer;
 import java.util.Objects;
 
-public class UpdateBuildInfoPlan extends ConfigPhysicalPlan {
+public class UpdateVersionInfoPlan extends ConfigPhysicalPlan {
 
   private int nodeId;
-  private String buildInfo;
+  private TNodeVersionInfo versionInfo;
 
-  public UpdateBuildInfoPlan() {
-    super(ConfigPhysicalPlanType.UpdateBuildInfo);
+  public UpdateVersionInfoPlan() {
+    super(ConfigPhysicalPlanType.UpdateVersionInfo);
   }
 
-  public UpdateBuildInfoPlan(String buildInfo, int nodeId) {
+  public UpdateVersionInfoPlan(TNodeVersionInfo versionInfo, int nodeId) {
     this();
-    this.buildInfo = buildInfo;
+    this.versionInfo = versionInfo;
     this.nodeId = nodeId;
   }
 
-  public String getBuildInfo() {
-    return buildInfo;
+  public TNodeVersionInfo getVersionInfo() {
+    return versionInfo;
   }
 
   public int getNodeId() {
@@ -55,13 +56,16 @@
   protected void serializeImpl(DataOutputStream stream) throws IOException {
     ReadWriteIOUtils.write(getType().getPlanType(), stream);
     ReadWriteIOUtils.write(nodeId, stream);
-    ReadWriteIOUtils.write(buildInfo, stream);
+    ReadWriteIOUtils.write(versionInfo.getVersion(), stream);
+    ReadWriteIOUtils.write(versionInfo.getBuildInfo(), stream);
   }
 
   @Override
   protected void deserializeImpl(ByteBuffer buffer) {
     nodeId = ReadWriteIOUtils.readInt(buffer);
-    buildInfo = ReadWriteIOUtils.readString(buffer);
+    versionInfo =
+        new TNodeVersionInfo(
+            ReadWriteIOUtils.readString(buffer), ReadWriteIOUtils.readString(buffer));
   }
 
   @Override
@@ -72,15 +76,15 @@
     if (o == null || getClass() != o.getClass()) {
       return false;
     }
-    if (!getType().equals(((UpdateBuildInfoPlan) o).getType())) {
+    if (!getType().equals(((UpdateVersionInfoPlan) o).getType())) {
       return false;
     }
-    UpdateBuildInfoPlan that = (UpdateBuildInfoPlan) o;
-    return nodeId == that.nodeId && buildInfo.equals(that.buildInfo);
+    UpdateVersionInfoPlan that = (UpdateVersionInfoPlan) o;
+    return nodeId == that.nodeId && versionInfo.equals(that.versionInfo);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(nodeId, buildInfo);
+    return Objects.hash(nodeId, versionInfo);
   }
 }
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 69645b5..f1f6cbd 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
@@ -144,6 +144,7 @@
 import org.apache.iotdb.confignode.rpc.thrift.TGetTriggerTableResp;
 import org.apache.iotdb.confignode.rpc.thrift.TGetUDFTableResp;
 import org.apache.iotdb.confignode.rpc.thrift.TMigrateRegionReq;
+import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp;
 import org.apache.iotdb.confignode.rpc.thrift.TRegionMigrateResultReportReq;
 import org.apache.iotdb.confignode.rpc.thrift.TRegionRouteMapResp;
@@ -444,9 +445,9 @@
               .sorted(Comparator.comparingInt(TDataNodeLocation::getDataNodeId))
               .collect(Collectors.toList());
       Map<Integer, String> nodeStatus = getLoadManager().getNodeStatusWithReason();
-      Map<Integer, String> nodeBuildInfo = getNodeManager().getNodeBuildInfo();
+      Map<Integer, TNodeVersionInfo> nodeVersionInfo = getNodeManager().getNodeVersionInfo();
       return new TShowClusterResp(
-          status, configNodeLocations, dataNodeInfoLocations, nodeStatus, nodeBuildInfo);
+          status, configNodeLocations, dataNodeInfoLocations, nodeStatus, nodeVersionInfo);
     } else {
       return new TShowClusterResp(
           status, new ArrayList<>(), new ArrayList<>(), new HashMap<>(), new HashMap<>());
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
index 21f8124..c4b504a 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ProcedureManager.java
@@ -467,7 +467,7 @@
    */
   public void addConfigNode(TConfigNodeRegisterReq req) {
     AddConfigNodeProcedure addConfigNodeProcedure =
-        new AddConfigNodeProcedure(req.getConfigNodeLocation(), req.getBuildInfo());
+        new AddConfigNodeProcedure(req.getConfigNodeLocation(), req.getVersionInfo());
     this.executor.submitProcedure(addConfigNodeProcedure);
   }
 
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 c34a5d5..55d3ab0 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
@@ -40,7 +40,7 @@
 import org.apache.iotdb.confignode.consensus.request.read.datanode.GetDataNodeConfigurationPlan;
 import org.apache.iotdb.confignode.consensus.request.write.confignode.ApplyConfigNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.confignode.RemoveConfigNodePlan;
-import org.apache.iotdb.confignode.consensus.request.write.confignode.UpdateBuildInfoPlan;
+import org.apache.iotdb.confignode.consensus.request.write.confignode.UpdateVersionInfoPlan;
 import org.apache.iotdb.confignode.consensus.request.write.datanode.RegisterDataNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.datanode.RemoveDataNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.datanode.UpdateDataNodePlan;
@@ -70,6 +70,7 @@
 import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRestartReq;
 import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRestartResp;
 import org.apache.iotdb.confignode.rpc.thrift.TGlobalConfig;
+import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TRatisConfig;
 import org.apache.iotdb.confignode.rpc.thrift.TRuntimeConfiguration;
 import org.apache.iotdb.confignode.rpc.thrift.TSetDataNodeStatusReq;
@@ -251,10 +252,10 @@
     registerDataNodePlan.getDataNodeConfiguration().getLocation().setDataNodeId(dataNodeId);
     getConsensusManager().write(registerDataNodePlan);
 
-    // update datanode's buildInfo
-    UpdateBuildInfoPlan updateBuildInfoPlan =
-        new UpdateBuildInfoPlan(req.getBuildInfo(), dataNodeId);
-    getConsensusManager().write(updateBuildInfoPlan);
+    // update datanode's versionInfo
+    UpdateVersionInfoPlan updateVersionInfoPlan =
+        new UpdateVersionInfoPlan(req.getVersionInfo(), dataNodeId);
+    getConsensusManager().write(updateVersionInfoPlan);
 
     // Bind DataNode metrics
     PartitionMetrics.bindDataNodePartitionMetrics(
@@ -282,11 +283,12 @@
           new UpdateDataNodePlan(req.getDataNodeConfiguration());
       getConsensusManager().write(updateDataNodePlan);
     }
-    String recordBuildInfo = nodeInfo.getBuildInfo(nodeId);
-    if (!req.getBuildInfo().equals(recordBuildInfo)) {
-      // Update buildInfo when modified during restart
-      UpdateBuildInfoPlan updateBuildInfoPlan = new UpdateBuildInfoPlan(req.getBuildInfo(), nodeId);
-      getConsensusManager().write(updateBuildInfoPlan);
+    TNodeVersionInfo versionInfo = nodeInfo.getVersionInfo(nodeId);
+    if (!req.getVersionInfo().equals(versionInfo)) {
+      // Update versionInfo when modified during restart
+      UpdateVersionInfoPlan updateVersionInfoPlan =
+          new UpdateVersionInfoPlan(req.getVersionInfo(), nodeId);
+      getConsensusManager().write(updateVersionInfoPlan);
     }
 
     TDataNodeRestartResp resp = new TDataNodeRestartResp();
@@ -355,11 +357,12 @@
         .setConfigNodeId(nodeId);
   }
 
-  public TSStatus updateConfigNodeIfNecessary(int configNodeId, String buildInfo) {
-    String recordBuildInfo = nodeInfo.getBuildInfo(configNodeId);
-    if (!recordBuildInfo.equals(buildInfo)) {
-      // Update buildInfo when modified during restart
-      UpdateBuildInfoPlan updateConfigNodePlan = new UpdateBuildInfoPlan(buildInfo, configNodeId);
+  public TSStatus updateConfigNodeIfNecessary(int configNodeId, TNodeVersionInfo versionInfo) {
+    TNodeVersionInfo recordVersionInfo = nodeInfo.getVersionInfo(configNodeId);
+    if (!recordVersionInfo.equals(versionInfo)) {
+      // Update versionInfo when modified during restart
+      UpdateVersionInfoPlan updateConfigNodePlan =
+          new UpdateVersionInfoPlan(versionInfo, configNodeId);
       ConsensusWriteResponse result = getConsensusManager().write(updateConfigNodePlan);
       return result.getStatus();
     }
@@ -484,8 +487,8 @@
     return nodeInfo.getRegisteredConfigNodes();
   }
 
-  public Map<Integer, String> getNodeBuildInfo() {
-    return nodeInfo.getNodeBuildInfo();
+  public Map<Integer, TNodeVersionInfo> getNodeVersionInfo() {
+    return nodeInfo.getNodeVersionInfo();
   }
 
   public List<TConfigNodeInfo> getRegisteredConfigNodeInfoList() {
@@ -515,14 +518,15 @@
    * Only leader use this interface, record the new ConfigNode's information.
    *
    * @param configNodeLocation The new ConfigNode.
-   * @param buildInfo The new ConfigNode's buildInfo.
+   * @param versionInfo The new ConfigNode's versionInfo.
    */
-  public void applyConfigNode(TConfigNodeLocation configNodeLocation, String buildInfo) {
+  public void applyConfigNode(
+      TConfigNodeLocation configNodeLocation, TNodeVersionInfo versionInfo) {
     ApplyConfigNodePlan applyConfigNodePlan = new ApplyConfigNodePlan(configNodeLocation);
     getConsensusManager().write(applyConfigNodePlan);
-    UpdateBuildInfoPlan updateBuildInfoPlan =
-        new UpdateBuildInfoPlan(buildInfo, configNodeLocation.getConfigNodeId());
-    getConsensusManager().write(updateBuildInfoPlan);
+    UpdateVersionInfoPlan updateVersionInfoPlan =
+        new UpdateVersionInfoPlan(versionInfo, configNodeLocation.getConfigNodeId());
+    getConsensusManager().write(updateVersionInfoPlan);
   }
 
   /**
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java
index 205eece..2aff2dc 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java
@@ -51,7 +51,7 @@
 import org.apache.iotdb.confignode.consensus.request.read.trigger.GetTriggerTablePlan;
 import org.apache.iotdb.confignode.consensus.request.write.confignode.ApplyConfigNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.confignode.RemoveConfigNodePlan;
-import org.apache.iotdb.confignode.consensus.request.write.confignode.UpdateBuildInfoPlan;
+import org.apache.iotdb.confignode.consensus.request.write.confignode.UpdateVersionInfoPlan;
 import org.apache.iotdb.confignode.consensus.request.write.cq.ActiveCQPlan;
 import org.apache.iotdb.confignode.consensus.request.write.cq.AddCQPlan;
 import org.apache.iotdb.confignode.consensus.request.write.cq.DropCQPlan;
@@ -364,8 +364,8 @@
         return nodeInfo.applyConfigNode((ApplyConfigNodePlan) physicalPlan);
       case RemoveConfigNode:
         return nodeInfo.removeConfigNode((RemoveConfigNodePlan) physicalPlan);
-      case UpdateBuildInfo:
-        return nodeInfo.updateBuildInfo((UpdateBuildInfoPlan) physicalPlan);
+      case UpdateVersionInfo:
+        return nodeInfo.updateVersionInfo((UpdateVersionInfoPlan) physicalPlan);
       case CreateFunction:
         return udfInfo.addUDFInTable((CreateFunctionPlan) physicalPlan);
       case DropFunction:
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/node/NodeInfo.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/node/NodeInfo.java
index d5300f8..aa5e0ed 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/node/NodeInfo.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/node/NodeInfo.java
@@ -28,11 +28,12 @@
 import org.apache.iotdb.confignode.consensus.request.read.datanode.GetDataNodeConfigurationPlan;
 import org.apache.iotdb.confignode.consensus.request.write.confignode.ApplyConfigNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.confignode.RemoveConfigNodePlan;
-import org.apache.iotdb.confignode.consensus.request.write.confignode.UpdateBuildInfoPlan;
+import org.apache.iotdb.confignode.consensus.request.write.confignode.UpdateVersionInfoPlan;
 import org.apache.iotdb.confignode.consensus.request.write.datanode.RegisterDataNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.datanode.RemoveDataNodePlan;
 import org.apache.iotdb.confignode.consensus.request.write.datanode.UpdateDataNodePlan;
 import org.apache.iotdb.confignode.consensus.response.datanode.DataNodeConfigurationResp;
+import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
 import org.apache.iotdb.rpc.TSStatusCode;
 import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
 
@@ -88,12 +89,12 @@
   // Registered DataNodes
   private final ReentrantReadWriteLock dataNodeInfoReadWriteLock;
 
-  private final ReentrantReadWriteLock buildInfoReadWriteLock;
+  private final ReentrantReadWriteLock versionInfoReadWriteLock;
 
   private final AtomicInteger nextNodeId = new AtomicInteger(-1);
   private final Map<Integer, TDataNodeConfiguration> registeredDataNodes;
 
-  private final Map<Integer, String> nodeBuildInfo;
+  private final Map<Integer, TNodeVersionInfo> nodeVersionInfo;
   private static final String SNAPSHOT_FILENAME = "node_info.bin";
 
   public NodeInfo() {
@@ -103,8 +104,8 @@
     this.dataNodeInfoReadWriteLock = new ReentrantReadWriteLock();
     this.registeredDataNodes = new ConcurrentHashMap<>();
 
-    this.nodeBuildInfo = new ConcurrentHashMap<>();
-    this.buildInfoReadWriteLock = new ReentrantReadWriteLock();
+    this.nodeVersionInfo = new ConcurrentHashMap<>();
+    this.versionInfoReadWriteLock = new ReentrantReadWriteLock();
   }
 
   /**
@@ -156,17 +157,17 @@
         registeredDataNodes.size());
 
     dataNodeInfoReadWriteLock.writeLock().lock();
-    buildInfoReadWriteLock.writeLock().lock();
+    versionInfoReadWriteLock.writeLock().lock();
     try {
       req.getDataNodeLocations()
           .forEach(
               removeDataNodes -> {
                 registeredDataNodes.remove(removeDataNodes.getDataNodeId());
-                nodeBuildInfo.remove(removeDataNodes.getDataNodeId());
+                nodeVersionInfo.remove(removeDataNodes.getDataNodeId());
                 LOGGER.info("Removed the datanode {} from cluster", removeDataNodes);
               });
     } finally {
-      buildInfoReadWriteLock.writeLock().unlock();
+      versionInfoReadWriteLock.writeLock().unlock();
       dataNodeInfoReadWriteLock.writeLock().unlock();
     }
     LOGGER.info(
@@ -336,10 +337,10 @@
   public TSStatus removeConfigNode(RemoveConfigNodePlan removeConfigNodePlan) {
     TSStatus status = new TSStatus();
     configNodeInfoReadWriteLock.writeLock().lock();
-    buildInfoReadWriteLock.writeLock().lock();
+    versionInfoReadWriteLock.writeLock().lock();
     try {
       registeredConfigNodes.remove(removeConfigNodePlan.getConfigNodeLocation().getConfigNodeId());
-      nodeBuildInfo.remove(removeConfigNodePlan.getConfigNodeLocation().getConfigNodeId());
+      nodeVersionInfo.remove(removeConfigNodePlan.getConfigNodeLocation().getConfigNodeId());
       SystemPropertiesUtils.storeConfigNodeList(new ArrayList<>(registeredConfigNodes.values()));
       LOGGER.info(
           "Successfully remove ConfigNode: {}. Current ConfigNodeGroup: {}",
@@ -352,26 +353,27 @@
       status.setMessage(
           "Remove ConfigNode failed because current ConfigNode can't store ConfigNode information.");
     } finally {
-      buildInfoReadWriteLock.writeLock().unlock();
+      versionInfoReadWriteLock.writeLock().unlock();
       configNodeInfoReadWriteLock.writeLock().unlock();
     }
     return status;
   }
 
   /**
-   * Update the specified Node‘s buildInfo.
+   * Update the specified Node‘s versionInfo.
    *
-   * @param updateBuildInfoPlan UpdateBuildInfoPlan
+   * @param updateVersionInfoPlan UpdateVersionInfoPlan
    * @return {@link TSStatusCode#SUCCESS_STATUS} if update build info successfully.
    */
-  public TSStatus updateBuildInfo(UpdateBuildInfoPlan updateBuildInfoPlan) {
-    buildInfoReadWriteLock.writeLock().lock();
+  public TSStatus updateVersionInfo(UpdateVersionInfoPlan updateVersionInfoPlan) {
+    versionInfoReadWriteLock.writeLock().lock();
     try {
-      nodeBuildInfo.put(updateBuildInfoPlan.getNodeId(), updateBuildInfoPlan.getBuildInfo());
+      nodeVersionInfo.put(
+          updateVersionInfoPlan.getNodeId(), updateVersionInfoPlan.getVersionInfo());
     } finally {
-      buildInfoReadWriteLock.writeLock().unlock();
+      versionInfoReadWriteLock.writeLock().unlock();
     }
-    LOGGER.info("Successfully update Node {} 's buildInfo.", updateBuildInfoPlan.getNodeId());
+    LOGGER.info("Successfully update Node {} 's version.", updateVersionInfoPlan.getNodeId());
     return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
   }
 
@@ -405,23 +407,23 @@
   }
 
   /** @return all nodes buildInfo */
-  public Map<Integer, String> getNodeBuildInfo() {
-    Map<Integer, String> result = new HashMap<>();
-    buildInfoReadWriteLock.readLock().lock();
+  public Map<Integer, TNodeVersionInfo> getNodeVersionInfo() {
+    Map<Integer, TNodeVersionInfo> result = new HashMap<>(nodeVersionInfo.size());
+    versionInfoReadWriteLock.readLock().lock();
     try {
-      result.putAll(nodeBuildInfo);
+      result.putAll(nodeVersionInfo);
     } finally {
-      buildInfoReadWriteLock.readLock().unlock();
+      versionInfoReadWriteLock.readLock().unlock();
     }
     return result;
   }
 
-  public String getBuildInfo(int nodeId) {
-    buildInfoReadWriteLock.readLock().lock();
+  public TNodeVersionInfo getVersionInfo(int nodeId) {
+    versionInfoReadWriteLock.readLock().lock();
     try {
-      return nodeBuildInfo.getOrDefault(nodeId, "Unknown");
+      return nodeVersionInfo.getOrDefault(nodeId, new TNodeVersionInfo("Unknown", "Unknown"));
     } finally {
-      buildInfoReadWriteLock.readLock().unlock();
+      versionInfoReadWriteLock.readLock().unlock();
     }
   }
 
@@ -442,7 +444,7 @@
     File tmpFile = new File(snapshotFile.getAbsolutePath() + "-" + UUID.randomUUID());
     configNodeInfoReadWriteLock.readLock().lock();
     dataNodeInfoReadWriteLock.readLock().lock();
-    buildInfoReadWriteLock.readLock().lock();
+    versionInfoReadWriteLock.readLock().lock();
     try (FileOutputStream fileOutputStream = new FileOutputStream(tmpFile);
         TIOStreamTransport tioStreamTransport = new TIOStreamTransport(fileOutputStream)) {
 
@@ -454,7 +456,7 @@
 
       serializeRegisteredDataNode(fileOutputStream, protocol);
 
-      serializeBuildInfo(fileOutputStream);
+      serializeVersionInfo(fileOutputStream);
 
       fileOutputStream.flush();
 
@@ -463,7 +465,7 @@
       return tmpFile.renameTo(snapshotFile);
 
     } finally {
-      buildInfoReadWriteLock.readLock().unlock();
+      versionInfoReadWriteLock.readLock().unlock();
       dataNodeInfoReadWriteLock.readLock().unlock();
       configNodeInfoReadWriteLock.readLock().unlock();
       for (int retry = 0; retry < 5; retry++) {
@@ -495,11 +497,12 @@
     }
   }
 
-  private void serializeBuildInfo(OutputStream outputStream) throws IOException {
-    ReadWriteIOUtils.write(nodeBuildInfo.size(), outputStream);
-    for (Entry<Integer, String> entry : nodeBuildInfo.entrySet()) {
+  private void serializeVersionInfo(OutputStream outputStream) throws IOException {
+    ReadWriteIOUtils.write(nodeVersionInfo.size(), outputStream);
+    for (Entry<Integer, TNodeVersionInfo> entry : nodeVersionInfo.entrySet()) {
       ReadWriteIOUtils.write(entry.getKey(), outputStream);
-      ReadWriteIOUtils.write(entry.getValue(), outputStream);
+      ReadWriteIOUtils.write(entry.getValue().getVersion(), outputStream);
+      ReadWriteIOUtils.write(entry.getValue().getBuildInfo(), outputStream);
     }
   }
 
@@ -516,7 +519,7 @@
 
     configNodeInfoReadWriteLock.writeLock().lock();
     dataNodeInfoReadWriteLock.writeLock().lock();
-    buildInfoReadWriteLock.writeLock().lock();
+    versionInfoReadWriteLock.writeLock().lock();
 
     try (FileInputStream fileInputStream = new FileInputStream(snapshotFile);
         TIOStreamTransport tioStreamTransport = new TIOStreamTransport(fileInputStream)) {
@@ -533,7 +536,7 @@
       deserializeBuildInfo(fileInputStream);
 
     } finally {
-      buildInfoReadWriteLock.writeLock().unlock();
+      versionInfoReadWriteLock.writeLock().unlock();
       dataNodeInfoReadWriteLock.writeLock().unlock();
       configNodeInfoReadWriteLock.writeLock().unlock();
     }
@@ -570,8 +573,9 @@
       int size = ReadWriteIOUtils.readInt(inputStream);
       while (size > 0) {
         int nodeId = ReadWriteIOUtils.readInt(inputStream);
+        String version = ReadWriteIOUtils.readString(inputStream);
         String buildInfo = ReadWriteIOUtils.readString(inputStream);
-        nodeBuildInfo.put(nodeId, buildInfo);
+        nodeVersionInfo.put(nodeId, new TNodeVersionInfo(version, buildInfo));
         size--;
       }
     }
@@ -585,7 +589,7 @@
     nextNodeId.set(-1);
     registeredDataNodes.clear();
     registeredConfigNodes.clear();
-    nodeBuildInfo.clear();
+    nodeVersionInfo.clear();
   }
 
   @Override
@@ -600,11 +604,11 @@
     return registeredConfigNodes.equals(nodeInfo.registeredConfigNodes)
         && nextNodeId.get() == nodeInfo.nextNodeId.get()
         && registeredDataNodes.equals(nodeInfo.registeredDataNodes)
-        && nodeBuildInfo.equals(nodeInfo.nodeBuildInfo);
+        && nodeVersionInfo.equals(nodeInfo.nodeVersionInfo);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(registeredConfigNodes, nextNodeId, registeredDataNodes, nodeBuildInfo);
+    return Objects.hash(registeredConfigNodes, nextNodeId, registeredDataNodes, nodeVersionInfo);
   }
 }
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/ConfigNodeProcedureEnv.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/ConfigNodeProcedureEnv.java
index df6c329..371243b 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/ConfigNodeProcedureEnv.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/env/ConfigNodeProcedureEnv.java
@@ -57,6 +57,7 @@
 import org.apache.iotdb.confignode.procedure.scheduler.LockQueue;
 import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler;
 import org.apache.iotdb.confignode.rpc.thrift.TAddConsensusGroupReq;
+import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
 import org.apache.iotdb.mpp.rpc.thrift.TActiveTriggerInstanceReq;
 import org.apache.iotdb.mpp.rpc.thrift.TCreateDataRegionReq;
 import org.apache.iotdb.mpp.rpc.thrift.TCreatePipePluginInstanceReq;
@@ -324,10 +325,11 @@
    * Leader will record the new ConfigNode's information.
    *
    * @param configNodeLocation The new ConfigNode
-   * @param buildInfo The new ConfigNode's buildInfo
+   * @param versionInfo The new ConfigNode's versionInfo
    */
-  public void applyConfigNode(TConfigNodeLocation configNodeLocation, String buildInfo) {
-    configManager.getNodeManager().applyConfigNode(configNodeLocation, buildInfo);
+  public void applyConfigNode(
+      TConfigNodeLocation configNodeLocation, TNodeVersionInfo versionInfo) {
+    configManager.getNodeManager().applyConfigNode(configNodeLocation, versionInfo);
   }
 
   /**
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/node/AddConfigNodeProcedure.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/node/AddConfigNodeProcedure.java
index d09d331..f14cc08 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/node/AddConfigNodeProcedure.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/node/AddConfigNodeProcedure.java
@@ -26,6 +26,7 @@
 import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
 import org.apache.iotdb.confignode.procedure.state.AddConfigNodeState;
 import org.apache.iotdb.confignode.procedure.store.ProcedureType;
+import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
 import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
 
 import org.slf4j.Logger;
@@ -43,16 +44,17 @@
 
   private TConfigNodeLocation tConfigNodeLocation;
 
-  private String buildInfo;
+  private TNodeVersionInfo versionInfo;
 
   public AddConfigNodeProcedure() {
     super();
   }
 
-  public AddConfigNodeProcedure(TConfigNodeLocation tConfigNodeLocation, String buildInfo) {
+  public AddConfigNodeProcedure(
+      TConfigNodeLocation tConfigNodeLocation, TNodeVersionInfo versionInfo) {
     super();
     this.tConfigNodeLocation = tConfigNodeLocation;
-    this.buildInfo = buildInfo;
+    this.versionInfo = versionInfo;
   }
 
   @Override
@@ -79,7 +81,7 @@
           break;
         case REGISTER_SUCCESS:
           env.notifyRegisterSuccess(tConfigNodeLocation);
-          env.applyConfigNode(tConfigNodeLocation, buildInfo);
+          env.applyConfigNode(tConfigNodeLocation, versionInfo);
           env.broadCastTheLatestConfigNodeGroup();
           LOG.info("The ConfigNode: {} is successfully added to the cluster", tConfigNodeLocation);
           return Flow.NO_MORE_STATE;
@@ -149,7 +151,8 @@
     stream.writeShort(ProcedureType.ADD_CONFIG_NODE_PROCEDURE.getTypeCode());
     super.serialize(stream);
     ThriftConfigNodeSerDeUtils.serializeTConfigNodeLocation(tConfigNodeLocation, stream);
-    ReadWriteIOUtils.write(buildInfo, stream);
+    ReadWriteIOUtils.write(versionInfo.getVersion(), stream);
+    ReadWriteIOUtils.write(versionInfo.getBuildInfo(), stream);
   }
 
   @Override
@@ -157,10 +160,14 @@
     super.deserialize(byteBuffer);
     try {
       tConfigNodeLocation = ThriftConfigNodeSerDeUtils.deserializeTConfigNodeLocation(byteBuffer);
-      // old version may not have build info,
+      // old version may not have version info,
       // thus we need to check inputStream before deserialize.
       if (byteBuffer.hasRemaining()) {
-        buildInfo = ReadWriteIOUtils.readString(byteBuffer);
+        versionInfo =
+            new TNodeVersionInfo(
+                ReadWriteIOUtils.readString(byteBuffer), ReadWriteIOUtils.readString(byteBuffer));
+      } else {
+        versionInfo = new TNodeVersionInfo("Unknown", "Unknown");
       }
     } catch (ThriftSerDeException e) {
       LOG.error("Error in deserialize AddConfigNodeProcedure", e);
@@ -174,13 +181,13 @@
       return thatProc.getProcId() == this.getProcId()
           && thatProc.getState() == this.getState()
           && thatProc.tConfigNodeLocation.equals(this.tConfigNodeLocation)
-          && thatProc.buildInfo.equals(this.buildInfo);
+          && thatProc.versionInfo.equals(this.versionInfo);
     }
     return false;
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(this.tConfigNodeLocation, this.buildInfo);
+    return Objects.hash(this.tConfigNodeLocation, this.versionInfo);
   }
 }
diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/ConfigNode.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/ConfigNode.java
index 40314a5..8caacd2 100644
--- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/ConfigNode.java
+++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/ConfigNode.java
@@ -43,6 +43,7 @@
 import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterReq;
 import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRegisterResp;
 import org.apache.iotdb.confignode.rpc.thrift.TConfigNodeRestartReq;
+import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
 import org.apache.iotdb.confignode.service.thrift.ConfigNodeRPCService;
 import org.apache.iotdb.confignode.service.thrift.ConfigNodeRPCServiceProcessor;
 import org.apache.iotdb.db.service.metrics.ProcessMetrics;
@@ -150,7 +151,9 @@
           TSStatus status =
               configManager
                   .getNodeManager()
-                  .updateConfigNodeIfNecessary(configNodeId, IoTDBConstant.BUILD_INFO);
+                  .updateConfigNodeIfNecessary(
+                      configNodeId,
+                      new TNodeVersionInfo(IoTDBConstant.VERSION, IoTDBConstant.BUILD_INFO));
           if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
             break;
           } else {
@@ -178,7 +181,8 @@
         configManager
             .getNodeManager()
             .applyConfigNode(
-                generateConfigNodeLocation(SEED_CONFIG_NODE_ID), IoTDBConstant.BUILD_INFO);
+                generateConfigNodeLocation(SEED_CONFIG_NODE_ID),
+                new TNodeVersionInfo(IoTDBConstant.VERSION, IoTDBConstant.BUILD_INFO));
         setUpMetricService();
         // Notice: We always set up Seed-ConfigNode's RPC service lastly to ensure
         // that the external service is not provided until Seed-ConfigNode is fully initialized
@@ -308,7 +312,7 @@
             configManager.getClusterParameters(),
             generateConfigNodeLocation(INIT_NON_SEED_CONFIG_NODE_ID));
 
-    req.setBuildInfo(IoTDBConstant.BUILD_INFO);
+    req.setVersionInfo(new TNodeVersionInfo(IoTDBConstant.VERSION, IoTDBConstant.BUILD_INFO));
 
     TEndPoint targetConfigNode = CONF.getTargetConfigNode();
     if (targetConfigNode == null) {
diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/node/AddConfigNodeProcedureTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/node/AddConfigNodeProcedureTest.java
index d95d49c..b6eaa31 100644
--- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/node/AddConfigNodeProcedureTest.java
+++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/node/AddConfigNodeProcedureTest.java
@@ -22,6 +22,7 @@
 import org.apache.iotdb.common.rpc.thrift.TEndPoint;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.confignode.procedure.store.ProcedureFactory;
+import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
 import org.apache.iotdb.tsfile.utils.PublicBAOS;
 
 import org.junit.Assert;
@@ -39,7 +40,7 @@
         new AddConfigNodeProcedure(
             new TConfigNodeLocation(
                 0, new TEndPoint("127.0.0.1", 10710), new TEndPoint("0.0.0.0", 10720)),
-            IoTDBConstant.BUILD_INFO);
+            new TNodeVersionInfo(IoTDBConstant.VERSION, IoTDBConstant.BUILD_INFO));
 
     try (PublicBAOS byteArrayOutputStream = new PublicBAOS();
         DataOutputStream outputStream = new DataOutputStream(byteArrayOutputStream)) {
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/ColumnHeaderConstant.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/ColumnHeaderConstant.java
index 7035a2b..5cd5e9a 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/ColumnHeaderConstant.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/ColumnHeaderConstant.java
@@ -330,6 +330,7 @@
           new ColumnHeader(STATUS, TSDataType.TEXT),
           new ColumnHeader(INTERNAL_ADDRESS, TSDataType.TEXT),
           new ColumnHeader(INTERNAL_PORT, TSDataType.INT32),
+          new ColumnHeader(VERSION, TSDataType.TEXT),
           new ColumnHeader(BUILD_INFO, TSDataType.TEXT));
 
   public static final List<ColumnHeader> showClusterDetailsColumnHeaders =
@@ -344,7 +345,9 @@
           new ColumnHeader(RPC_PORT, TSDataType.TEXT),
           new ColumnHeader(MPP_PORT, TSDataType.TEXT),
           new ColumnHeader(SCHEMA_CONSENSUS_PORT, TSDataType.TEXT),
-          new ColumnHeader(DATA_CONSENSUS_PORT, TSDataType.TEXT));
+          new ColumnHeader(DATA_CONSENSUS_PORT, TSDataType.TEXT),
+          new ColumnHeader(VERSION, TSDataType.TEXT),
+          new ColumnHeader(BUILD_INFO, TSDataType.TEXT));
 
   public static final List<ColumnHeader> showVariablesColumnHeaders =
       ImmutableList.of(
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterDetailsTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterDetailsTask.java
index fb10d52..65b7b3c 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterDetailsTask.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterDetailsTask.java
@@ -19,6 +19,7 @@
 
 package org.apache.iotdb.db.queryengine.plan.execution.config.metadata;
 
+import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TShowClusterResp;
 import org.apache.iotdb.db.queryengine.common.header.ColumnHeader;
 import org.apache.iotdb.db.queryengine.common.header.ColumnHeaderConstant;
@@ -53,14 +54,14 @@
   private static void buildConfigNodesTsBlock(
       TsBlockBuilder builder,
       int nodeId,
-      String nodeType,
       String nodeStatus,
       String internalAddress,
       int internalPort,
-      int configConsensusPort) {
+      int configConsensusPort,
+      TNodeVersionInfo versionInfo) {
     builder.getTimeColumnBuilder().writeLong(0L);
     builder.getColumnBuilder(0).writeInt(nodeId);
-    builder.getColumnBuilder(1).writeBinary(new Binary(nodeType));
+    builder.getColumnBuilder(1).writeBinary(new Binary(NODE_TYPE_CONFIG_NODE));
     builder.getColumnBuilder(2).writeBinary(new Binary(nodeStatus));
     builder.getColumnBuilder(3).writeBinary(new Binary(internalAddress));
     builder.getColumnBuilder(4).writeInt(internalPort);
@@ -70,6 +71,8 @@
     builder.getColumnBuilder(8).writeBinary(new Binary(""));
     builder.getColumnBuilder(9).writeBinary(new Binary(""));
     builder.getColumnBuilder(10).writeBinary(new Binary(""));
+    builder.getColumnBuilder(11).writeBinary(new Binary(versionInfo.getVersion()));
+    builder.getColumnBuilder(12).writeBinary(new Binary(versionInfo.getBuildInfo()));
     builder.declarePosition();
   }
 
@@ -77,7 +80,6 @@
   private static void buildDataNodesTsBlock(
       TsBlockBuilder builder,
       int nodeId,
-      String nodeType,
       String nodeStatus,
       String internalAddress,
       int internalPort,
@@ -85,10 +87,11 @@
       int rpcPort,
       int dataConsensusPort,
       int schemaConsensusPort,
-      int mppPort) {
+      int mppPort,
+      TNodeVersionInfo versionInfo) {
     builder.getTimeColumnBuilder().writeLong(0L);
     builder.getColumnBuilder(0).writeInt(nodeId);
-    builder.getColumnBuilder(1).writeBinary(new Binary(nodeType));
+    builder.getColumnBuilder(1).writeBinary(new Binary(NODE_TYPE_DATA_NODE));
     builder.getColumnBuilder(2).writeBinary(new Binary(nodeStatus));
     builder.getColumnBuilder(3).writeBinary(new Binary(internalAddress));
     builder.getColumnBuilder(4).writeInt(internalPort);
@@ -98,6 +101,8 @@
     builder.getColumnBuilder(8).writeBinary(new Binary(Integer.toString(dataConsensusPort)));
     builder.getColumnBuilder(9).writeBinary(new Binary(Integer.toString(schemaConsensusPort)));
     builder.getColumnBuilder(10).writeBinary(new Binary(Integer.toString(mppPort)));
+    builder.getColumnBuilder(11).writeBinary(new Binary(versionInfo.getVersion()));
+    builder.getColumnBuilder(12).writeBinary(new Binary(versionInfo.getBuildInfo()));
     builder.declarePosition();
   }
 
@@ -116,11 +121,11 @@
                 buildConfigNodesTsBlock(
                     builder,
                     e.getConfigNodeId(),
-                    NODE_TYPE_CONFIG_NODE,
                     clusterNodeInfos.getNodeStatus().get(e.getConfigNodeId()),
                     e.getInternalEndPoint().getIp(),
                     e.getInternalEndPoint().getPort(),
-                    e.getConsensusEndPoint().getPort()));
+                    e.getConsensusEndPoint().getPort(),
+                    clusterNodeInfos.getNodeVersionInfo().get(e.getConfigNodeId())));
 
     clusterNodeInfos
         .getDataNodeList()
@@ -129,7 +134,6 @@
                 buildDataNodesTsBlock(
                     builder,
                     e.getDataNodeId(),
-                    NODE_TYPE_DATA_NODE,
                     clusterNodeInfos.getNodeStatus().get(e.getDataNodeId()),
                     e.getInternalEndPoint().getIp(),
                     e.getInternalEndPoint().getPort(),
@@ -137,7 +141,8 @@
                     e.getClientRpcEndPoint().getPort(),
                     e.getMPPDataExchangeEndPoint().getPort(),
                     e.getSchemaRegionConsensusEndPoint().getPort(),
-                    e.getDataRegionConsensusEndPoint().getPort()));
+                    e.getDataRegionConsensusEndPoint().getPort(),
+                    clusterNodeInfos.getNodeVersionInfo().get(e.getDataNodeId())));
 
     DatasetHeader datasetHeader = DatasetHeaderFactory.getShowClusterDetailsHeader();
     future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS, builder.build(), datasetHeader));
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterTask.java
index 71db942..65ebb45 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterTask.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/ShowClusterTask.java
@@ -19,6 +19,7 @@
 
 package org.apache.iotdb.db.queryengine.plan.execution.config.metadata;
 
+import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TShowClusterResp;
 import org.apache.iotdb.db.queryengine.common.header.ColumnHeader;
 import org.apache.iotdb.db.queryengine.common.header.ColumnHeaderConstant;
@@ -63,14 +64,15 @@
       String nodeStatus,
       String hostAddress,
       int port,
-      String buildInfo) {
+      TNodeVersionInfo versionInfo) {
     builder.getTimeColumnBuilder().writeLong(0L);
     builder.getColumnBuilder(0).writeInt(nodeId);
     builder.getColumnBuilder(1).writeBinary(new Binary(nodeType));
     builder.getColumnBuilder(2).writeBinary(new Binary(nodeStatus));
     builder.getColumnBuilder(3).writeBinary(new Binary(hostAddress));
     builder.getColumnBuilder(4).writeInt(port);
-    builder.getColumnBuilder(5).writeBinary(new Binary(buildInfo));
+    builder.getColumnBuilder(5).writeBinary(new Binary(versionInfo.getVersion()));
+    builder.getColumnBuilder(6).writeBinary(new Binary(versionInfo.getBuildInfo()));
     builder.declarePosition();
   }
 
@@ -93,7 +95,7 @@
                     clusterNodeInfos.getNodeStatus().get(e.getConfigNodeId()),
                     e.getInternalEndPoint().getIp(),
                     e.getInternalEndPoint().getPort(),
-                    clusterNodeInfos.getNodeBuildInfo().get(e.getConfigNodeId())));
+                    clusterNodeInfos.getNodeVersionInfo().get(e.getConfigNodeId())));
 
     clusterNodeInfos
         .getDataNodeList()
@@ -106,7 +108,7 @@
                     clusterNodeInfos.getNodeStatus().get(e.getDataNodeId()),
                     e.getInternalEndPoint().getIp(),
                     e.getInternalEndPoint().getPort(),
-                    clusterNodeInfos.getNodeBuildInfo().get(e.getDataNodeId())));
+                    clusterNodeInfos.getNodeVersionInfo().get(e.getDataNodeId())));
 
     DatasetHeader datasetHeader = DatasetHeaderFactory.getShowClusterHeader();
     future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS, builder.build(), datasetHeader));
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java
index fce5216..7aa275a 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java
@@ -48,6 +48,7 @@
 import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRestartResp;
 import org.apache.iotdb.confignode.rpc.thrift.TGetJarInListReq;
 import org.apache.iotdb.confignode.rpc.thrift.TGetJarInListResp;
+import org.apache.iotdb.confignode.rpc.thrift.TNodeVersionInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TRuntimeConfiguration;
 import org.apache.iotdb.confignode.rpc.thrift.TSystemConfigurationResp;
 import org.apache.iotdb.consensus.ConsensusFactory;
@@ -361,7 +362,7 @@
     TDataNodeRegisterReq req = new TDataNodeRegisterReq();
     req.setDataNodeConfiguration(generateDataNodeConfiguration());
     req.setClusterName(config.getClusterName());
-    req.setBuildInfo(IoTDBConstant.BUILD_INFO);
+    req.setVersionInfo(new TNodeVersionInfo(IoTDBConstant.VERSION, IoTDBConstant.BUILD_INFO));
     TDataNodeRegisterResp dataNodeRegisterResp = null;
     while (retry > 0) {
       try (ConfigNodeClient configNodeClient =
@@ -421,7 +422,7 @@
     req.setClusterName(
         config.getClusterName() == null ? DEFAULT_CLUSTER_NAME : config.getClusterName());
     req.setDataNodeConfiguration(generateDataNodeConfiguration());
-    req.setBuildInfo(IoTDBConstant.BUILD_INFO);
+    req.setVersionInfo(new TNodeVersionInfo(IoTDBConstant.VERSION, IoTDBConstant.BUILD_INFO));
     TDataNodeRestartResp dataNodeRestartResp = null;
     while (retry > 0) {
       try (ConfigNodeClient configNodeClient =
diff --git a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift
index b4b8cd9..dff1a07 100644
--- a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift
+++ b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift
@@ -102,7 +102,7 @@
 struct TDataNodeRegisterReq {
   1: required string clusterName
   2: required common.TDataNodeConfiguration dataNodeConfiguration
-  3: optional string buildInfo = "Unknown"
+  3: optional TNodeVersionInfo versionInfo
 }
 
 struct TDataNodeRegisterResp {
@@ -115,7 +115,7 @@
 struct TDataNodeRestartReq {
   1: required string clusterName
   2: required common.TDataNodeConfiguration dataNodeConfiguration
-  3: optional string buildInfo = "Unknown"
+  3: optional TNodeVersionInfo versionInfo
 }
 
 struct TDataNodeRestartResp {
@@ -368,7 +368,7 @@
   // fields are consistent with the Seed-ConfigNode
   1: required TClusterParameters clusterParameters
   2: required common.TConfigNodeLocation configNodeLocation
-  3: optional string buildInfo = "Unknown"
+  3: optional TNodeVersionInfo versionInfo
 }
 
 struct TConfigNodeRegisterResp {
@@ -486,7 +486,12 @@
   2: required list<common.TConfigNodeLocation> configNodeList
   3: required list<common.TDataNodeLocation> dataNodeList
   4: required map<i32, string> nodeStatus
-  5: required map<i32, string> nodeBuildInfo
+  5: required map<i32, TNodeVersionInfo> nodeVersionInfo
+}
+
+struct TNodeVersionInfo {
+  1: required string version;
+  2: required string buildInfo;
 }
 
 struct TShowVariablesResp {