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

jt2594838 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 2fedd8a395d Add degraded status to show pipes (#18047)
2fedd8a395d is described below

commit 2fedd8a395dda65a0e8b9e1204e2bd2af70bf0c1
Author: Caideyipi <[email protected]>
AuthorDate: Tue Jun 30 19:04:56 2026 +0800

    Add degraded status to show pipes (#18047)
    
    * Add degraded status to show pipes
    
    * Update PipeRealtimeExtractTest.java
---
 .../relational/it/schema/IoTDBDatabaseIT.java      |   5 +-
 .../heartbeat/DataNodeHeartbeatHandler.java        |   3 +-
 .../response/pipe/task/PipeTableResp.java          |   4 +
 .../iotdb/confignode/manager/ConfigManager.java    |   3 +-
 .../pipe/agent/task/PipeConfigNodeTaskAgent.java   |   4 +
 .../runtime/PipeRuntimeCoordinator.java            |   6 +-
 .../runtime/heartbeat/PipeHeartbeat.java           |  22 ++-
 .../runtime/heartbeat/PipeHeartbeatParser.java     |   1 +
 .../runtime/heartbeat/PipeHeartbeatScheduler.java  |   6 +-
 .../consensus/response/pipe/PipeTableRespTest.java |  48 +++++++
 .../runtime/heartbeat/PipeHeartbeatParserTest.java | 112 ++++++++++++++-
 .../db/pipe/agent/task/PipeDataNodeTaskAgent.java  |  14 ++
 .../PipeRealtimeDataRegionHybridSource.java        |  96 ++++++++++---
 .../InformationSchemaContentSupplierFactory.java   |   5 +
 .../execution/config/sys/pipe/ShowPipeTask.java    |   5 +
 .../db/pipe/source/PipeRealtimeExtractTest.java    | 151 ++++++++++++++++++++-
 .../config/sys/pipe/ShowPipeTaskTest.java          |  72 ++++++++++
 .../commons/pipe/agent/task/PipeTaskAgent.java     |  22 +++
 .../pipe/agent/task/meta/PipeTemporaryMeta.java    |  29 +++-
 .../agent/task/meta/PipeTemporaryMetaInAgent.java  |  25 +++-
 .../task/meta/PipeTemporaryMetaInCoordinator.java  |  29 +++-
 .../schema/column/ColumnHeaderConstant.java        |   5 +-
 .../commons/schema/table/InformationSchema.java    |   3 +
 .../agent/task/meta/PipeTemporaryMetaTest.java     |  92 +++++++++++++
 .../thrift-commons/src/main/thrift/common.thrift   |   1 +
 .../src/main/thrift/confignode.thrift              |   1 +
 .../src/main/thrift/datanode.thrift                |   1 +
 27 files changed, 728 insertions(+), 37 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java
index 31cd0241f69..22483ab43a6 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java
@@ -551,7 +551,8 @@ public class IoTDBDatabaseIT {
                   "pipe_sink,STRING,ATTRIBUTE,",
                   "exception_message,STRING,ATTRIBUTE,",
                   "remaining_event_count,INT64,ATTRIBUTE,",
-                  "estimated_remaining_seconds,DOUBLE,ATTRIBUTE,")));
+                  "estimated_remaining_seconds,DOUBLE,ATTRIBUTE,",
+                  "is_degraded,BOOLEAN,ATTRIBUTE,")));
       TestUtils.assertResultSetEqual(
           statement.executeQuery("desc pipe_plugins"),
           "ColumnName,DataType,Category,",
@@ -673,7 +674,7 @@ public class IoTDBDatabaseIT {
       // Filter out not self-created pipes
       TestUtils.assertResultSetEqual(
           statement.executeQuery("select * from pipes"),
-          
"id,creation_time,state,pipe_source,pipe_processor,pipe_sink,exception_message,remaining_event_count,estimated_remaining_seconds,",
+          
"id,creation_time,state,pipe_source,pipe_processor,pipe_sink,exception_message,remaining_event_count,estimated_remaining_seconds,is_degraded,",
           Collections.emptySet());
 
       // No auth needed
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/heartbeat/DataNodeHeartbeatHandler.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/heartbeat/DataNodeHeartbeatHandler.java
index 52053cdab45..3114d60ca3a 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/heartbeat/DataNodeHeartbeatHandler.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/client/async/handlers/heartbeat/DataNodeHeartbeatHandler.java
@@ -184,7 +184,8 @@ public class DataNodeHeartbeatHandler implements 
AsyncMethodCallback<TDataNodeHe
           heartbeatResp.getPipeMetaList(),
           heartbeatResp.getPipeCompletedList(),
           heartbeatResp.getPipeRemainingEventCountList(),
-          heartbeatResp.getPipeRemainingTimeList());
+          heartbeatResp.getPipeRemainingTimeList(),
+          heartbeatResp.getPipeDegradedStatusList());
     }
   }
 
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/pipe/task/PipeTableResp.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/pipe/task/PipeTableResp.java
index 6a63ca50690..b33bb7d4d6d 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/pipe/task/PipeTableResp.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/pipe/task/PipeTableResp.java
@@ -283,6 +283,10 @@ public class PipeTableResp implements DataSet {
           canCalculateOnLocal ? -1 : temporaryMeta.getGlobalRemainingEvents());
       showPipeInfo.setEstimatedRemainingTime(
           canCalculateOnLocal ? -1 : temporaryMeta.getGlobalRemainingTime());
+      final Boolean isDegraded = temporaryMeta.getGlobalDegraded();
+      if (Objects.nonNull(isDegraded)) {
+        showPipeInfo.setIsDegraded(isDegraded);
+      }
       showPipeInfoList.add(showPipeInfo);
     }
 
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 da10bb22283..e9647551a30 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
@@ -3178,7 +3178,8 @@ public class ConfigManager implements IManager {
             resp.getPipeMetaList(),
             resp.getPipeCompletedList(),
             resp.getPipeRemainingEventCountList(),
-            resp.getPipeRemainingTimeList());
+            resp.getPipeRemainingTimeList(),
+            resp.getPipeDegradedStatusList());
     return StatusUtils.OK;
   }
 
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java
index 8703c906709..f23cde99d01 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java
@@ -28,6 +28,7 @@ import org.apache.iotdb.commons.pipe.agent.task.PipeTaskAgent;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMeta;
 import org.apache.iotdb.commons.pipe.config.PipeConfig;
 import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
 import org.apache.iotdb.confignode.i18n.ManagerMessages;
@@ -219,6 +220,7 @@ public class PipeConfigNodeTaskAgent extends PipeTaskAgent {
     final List<ByteBuffer> pipeMetaBinaryList = new ArrayList<>();
     final List<Long> pipeRemainingEventCountList = new ArrayList<>();
     final List<Double> pipeRemainingTimeList = new ArrayList<>();
+    final List<Integer> pipeDegradedStatusList = new ArrayList<>();
     try {
       for (final PipeMeta pipeMeta : pipeMetaKeeper.getPipeMetaList()) {
         pipeMetaBinaryList.add(pipeMeta.serialize());
@@ -233,6 +235,7 @@ public class PipeConfigNodeTaskAgent extends PipeTaskAgent {
 
         pipeRemainingEventCountList.add(remainingEventCount);
         pipeRemainingTimeList.add(estimatedRemainingTime);
+        
pipeDegradedStatusList.add(PipeTemporaryMeta.TS_FILE_EPOCH_DEGRADED_STATUS_UNKNOWN);
 
         logger.ifPresent(
             l ->
@@ -249,6 +252,7 @@ public class PipeConfigNodeTaskAgent extends PipeTaskAgent {
     resp.setPipeMetaList(pipeMetaBinaryList);
     resp.setPipeRemainingEventCountList(pipeRemainingEventCountList);
     resp.setPipeRemainingTimeList(pipeRemainingTimeList);
+    resp.setPipeDegradedStatusList(pipeDegradedStatusList);
   }
 
   @Override
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/PipeRuntimeCoordinator.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/PipeRuntimeCoordinator.java
index cfbc35a446c..d9a578c379e 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/PipeRuntimeCoordinator.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/PipeRuntimeCoordinator.java
@@ -95,13 +95,15 @@ public class PipeRuntimeCoordinator implements 
IClusterStatusSubscriber {
       final List<ByteBuffer> pipeMetaByteBufferListFromDataNode,
       /* @Nullable */ final List<Boolean> pipeCompletedListFromAgent,
       /* @Nullable */ final List<Long> pipeRemainingEventCountListFromAgent,
-      /* @Nullable */ final List<Double> pipeRemainingTimeListFromAgent) {
+      /* @Nullable */ final List<Double> pipeRemainingTimeListFromAgent,
+      /* @Nullable */ final List<Integer> pipeDegradedStatusListFromAgent) {
     pipeHeartbeatScheduler.parseHeartbeat(
         dataNodeId,
         new PipeHeartbeat(
             pipeMetaByteBufferListFromDataNode,
             pipeCompletedListFromAgent,
             pipeRemainingEventCountListFromAgent,
-            pipeRemainingTimeListFromAgent));
+            pipeRemainingTimeListFromAgent,
+            pipeDegradedStatusListFromAgent));
   }
 }
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeat.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeat.java
index e7e9d2cd97d..31e5020bf89 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeat.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeat.java
@@ -21,6 +21,7 @@ package 
org.apache.iotdb.confignode.manager.pipe.coordinator.runtime.heartbeat;
 
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMeta;
 
 import java.nio.ByteBuffer;
 import java.util.HashMap;
@@ -33,12 +34,14 @@ public class PipeHeartbeat {
   private final Map<PipeStaticMeta, Boolean> isCompletedMap = new HashMap<>();
   private final Map<PipeStaticMeta, Long> remainingEventCountMap = new 
HashMap<>();
   private final Map<PipeStaticMeta, Double> remainingTimeMap = new HashMap<>();
+  private final Map<PipeStaticMeta, Boolean> isDegradedMap = new HashMap<>();
 
   public PipeHeartbeat(
       final List<ByteBuffer> pipeMetaByteBufferListFromAgent,
       /* @Nullable */ final List<Boolean> pipeCompletedListFromAgent,
       /* @Nullable */ final List<Long> pipeRemainingEventCountListFromAgent,
-      /* @Nullable */ final List<Double> pipeRemainingTimeListFromAgent) {
+      /* @Nullable */ final List<Double> pipeRemainingTimeListFromAgent,
+      /* @Nullable */ final List<Integer> pipeDegradedStatusListFromAgent) {
     // Shall not reach here, just in case
     if (Objects.isNull(pipeMetaByteBufferListFromAgent)) {
       return;
@@ -49,20 +52,31 @@ public class PipeHeartbeat {
       pipeMetaMap.put(pipeMeta.getStaticMeta(), pipeMeta);
       isCompletedMap.put(
           pipeMeta.getStaticMeta(),
-          Objects.nonNull(pipeCompletedListFromAgent) && 
pipeCompletedListFromAgent.get(i));
+          Objects.nonNull(pipeCompletedListFromAgent)
+              && i < pipeCompletedListFromAgent.size()
+              && pipeCompletedListFromAgent.get(i));
       // If remaining event count & remaining time can not be got, it implies 
that the heartbeat is
       // from an ancient version of DataNode. Here we guarantee that "0" will 
not affect both of
       // the final results and namely these dataNodes are omitted in 
calculation.
       remainingEventCountMap.put(
           pipeMeta.getStaticMeta(),
           Objects.nonNull(pipeRemainingEventCountListFromAgent)
+                  && i < pipeRemainingEventCountListFromAgent.size()
               ? pipeRemainingEventCountListFromAgent.get(i)
               : 0L);
       remainingTimeMap.put(
           pipeMeta.getStaticMeta(),
           Objects.nonNull(pipeRemainingTimeListFromAgent)
+                  && i < pipeRemainingTimeListFromAgent.size()
               ? pipeRemainingTimeListFromAgent.get(i)
               : 0d);
+      isDegradedMap.put(
+          pipeMeta.getStaticMeta(),
+          PipeTemporaryMeta.decodeTsFileEpochDegradedStatus(
+              Objects.nonNull(pipeDegradedStatusListFromAgent)
+                      && i < pipeDegradedStatusListFromAgent.size()
+                  ? pipeDegradedStatusListFromAgent.get(i)
+                  : null));
     }
   }
 
@@ -86,6 +100,10 @@ public class PipeHeartbeat {
     return remainingTimeMap.get(pipeStaticMeta);
   }
 
+  public Boolean getDegraded(final PipeStaticMeta pipeStaticMeta) {
+    return isDegradedMap.get(pipeStaticMeta);
+  }
+
   public boolean isEmpty() {
     return pipeMetaMap.isEmpty();
   }
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeatParser.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeatParser.java
index 31f4119caaa..2a285671493 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeatParser.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeatParser.java
@@ -191,6 +191,7 @@ public class PipeHeartbeatParser {
       // Record statistics
       temporaryMeta.setRemainingEvent(nodeId, 
pipeHeartbeat.getRemainingEventCount(staticMeta));
       temporaryMeta.setRemainingTime(nodeId, 
pipeHeartbeat.getRemainingTime(staticMeta));
+      temporaryMeta.setDegraded(nodeId, pipeHeartbeat.getDegraded(staticMeta));
 
       final Map<Integer, PipeTaskMeta> pipeTaskMetaMapFromCoordinator =
           
pipeMetaFromCoordinator.getRuntimeMeta().getConsensusGroupId2TaskMetaMap();
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeatScheduler.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeatScheduler.java
index b9276880cea..00368c0472a 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeatScheduler.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeatScheduler.java
@@ -115,7 +115,8 @@ public class PipeHeartbeatScheduler {
                         resp.getPipeMetaList(),
                         resp.getPipeCompletedList(),
                         resp.getPipeRemainingEventCountList(),
-                        resp.getPipeRemainingTimeList())));
+                        resp.getPipeRemainingTimeList(),
+                        resp.getPipeDegradedStatusList())));
 
     // config node heartbeat
     try {
@@ -127,7 +128,8 @@ public class PipeHeartbeatScheduler {
               configNodeResp.getPipeMetaList(),
               null,
               configNodeResp.getPipeRemainingEventCountList(),
-              configNodeResp.getPipeRemainingTimeList()));
+              configNodeResp.getPipeRemainingTimeList(),
+              configNodeResp.getPipeDegradedStatusList()));
     } catch (final Exception e) {
       PipeLogger.log(
           LOGGER::warn, e, 
ManagerMessages.FAILED_TO_COLLECT_PIPE_META_LIST_FROM_CONFIG_NODE_TASK);
diff --git 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/consensus/response/pipe/PipeTableRespTest.java
 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/consensus/response/pipe/PipeTableRespTest.java
index f6a75d26ee4..093e5989bb0 100644
--- 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/consensus/response/pipe/PipeTableRespTest.java
+++ 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/consensus/response/pipe/PipeTableRespTest.java
@@ -24,12 +24,20 @@ import 
org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeRuntimeMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta;
+import 
org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMetaInCoordinator;
 import org.apache.iotdb.commons.pipe.config.constant.SystemConstant;
 import org.apache.iotdb.confignode.consensus.response.pipe.task.PipeTableResp;
+import org.apache.iotdb.confignode.manager.ConfigManager;
+import org.apache.iotdb.confignode.manager.node.NodeManager;
+import org.apache.iotdb.confignode.rpc.thrift.TShowPipeInfo;
+import org.apache.iotdb.confignode.service.ConfigNode;
 import org.apache.iotdb.rpc.TSStatusCode;
 
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mockito;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -40,6 +48,26 @@ import java.util.concurrent.ConcurrentMap;
 
 public class PipeTableRespTest {
 
+  private ConfigNode oldInstance;
+
+  @Before
+  public void setUp() {
+    oldInstance = ConfigNode.getInstance();
+    final ConfigNode configNode = Mockito.mock(ConfigNode.class);
+    final ConfigManager configManager = Mockito.mock(ConfigManager.class);
+    final NodeManager nodeManager = Mockito.mock(NodeManager.class);
+
+    Mockito.when(configNode.getConfigManager()).thenReturn(configManager);
+    Mockito.when(configManager.getNodeManager()).thenReturn(nodeManager);
+    Mockito.when(nodeManager.getRegisteredDataNodeCount()).thenReturn(2);
+    ConfigNode.setInstance(configNode);
+  }
+
+  @After
+  public void tearDown() {
+    ConfigNode.setInstance(oldInstance);
+  }
+
   public PipeTableResp constructPipeTableResp() {
     TSStatus status = new 
TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
     List<PipeMeta> pipeMetaList = new ArrayList<>();
@@ -120,6 +148,26 @@ public class PipeTableRespTest {
     Assert.assertEquals(3, allPipeTableResp.getAllPipeMeta().size());
   }
 
+  @Test
+  public void testConvertToTShowPipeRespIncludesDegradedStatus() {
+    final PipeTableResp pipeTableResp = constructPipeTableResp();
+    ((PipeTemporaryMetaInCoordinator) 
pipeTableResp.getAllPipeMeta().get(0).getTemporaryMeta())
+        .setDegraded(1, true);
+    ((PipeTemporaryMetaInCoordinator) 
pipeTableResp.getAllPipeMeta().get(1).getTemporaryMeta())
+        .setDegraded(1, false);
+
+    final List<TShowPipeInfo> showPipeResult =
+        pipeTableResp.convertToTShowPipeResp().getPipeInfoList();
+
+    Assert.assertEquals(3, showPipeResult.size());
+    Assert.assertEquals("testPipe", showPipeResult.get(0).getId());
+    Assert.assertTrue(showPipeResult.get(0).isSetIsDegraded());
+    Assert.assertTrue(showPipeResult.get(0).isIsDegraded());
+    Assert.assertTrue(showPipeResult.get(1).isSetIsDegraded());
+    Assert.assertFalse(showPipeResult.get(1).isIsDegraded());
+    Assert.assertFalse(showPipeResult.get(2).isSetIsDegraded());
+  }
+
   @Test
   public void testFilterByModelBeforeWhereClause() {
     TSStatus status = new 
TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
diff --git 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeatParserTest.java
 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeatParserTest.java
index d5a46d42c84..1021d60768b 100644
--- 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeatParserTest.java
+++ 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/heartbeat/PipeHeartbeatParserTest.java
@@ -20,6 +20,14 @@
 package org.apache.iotdb.confignode.manager.pipe.coordinator.runtime.heartbeat;
 
 import org.apache.iotdb.commons.conf.CommonDescriptor;
+import org.apache.iotdb.commons.consensus.index.impl.MinimumProgressIndex;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeRuntimeMeta;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMeta;
+import 
org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMetaInCoordinator;
+import 
org.apache.iotdb.confignode.consensus.request.write.pipe.task.CreatePipePlanV2;
 import org.apache.iotdb.confignode.manager.ConfigManager;
 import org.apache.iotdb.confignode.manager.ProcedureManager;
 import org.apache.iotdb.confignode.manager.node.NodeManager;
@@ -35,11 +43,14 @@ import org.mockito.Mockito;
 
 import java.lang.reflect.Field;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.never;
@@ -117,7 +128,79 @@ public class PipeHeartbeatParserTest {
     verify(context.procedureManager, times(2)).pipeHandleMetaChange(true, 
false);
   }
 
+  @Test
+  public void testParseHeartbeatRecordsPipeDegradedStatus() throws Exception {
+    
CommonDescriptor.getInstance().getConfig().setSeperatedPipeHeartbeatEnabled(false);
+
+    final PipeTaskInfo pipeTaskInfo = new PipeTaskInfo();
+    final PipeMeta pipeMeta = createPipeMeta();
+    pipeTaskInfo.createPipe(
+        new CreatePipePlanV2(pipeMeta.getStaticMeta(), 
pipeMeta.getRuntimeMeta()));
+
+    final ParserTestContext context = createParserTestContext(1, pipeTaskInfo);
+    context.parser.parseHeartbeat(
+        1,
+        new PipeHeartbeat(
+            Collections.singletonList(pipeMeta.serialize()),
+            Collections.singletonList(false),
+            Collections.singletonList(0L),
+            Collections.singletonList(0d),
+            
Collections.singletonList(PipeTemporaryMeta.TS_FILE_EPOCH_DEGRADED_STATUS_TRUE)));
+
+    assertEquals(Boolean.TRUE, 
getTemporaryMeta(pipeTaskInfo).getGlobalDegraded());
+    verify(context.procedureManager, 
never()).pipeHandleMetaChange(anyBoolean(), anyBoolean());
+  }
+
+  @Test
+  public void testParseHeartbeatAggregatesPipeDegradedStatusFromAllDataNodes() 
throws Exception {
+    
CommonDescriptor.getInstance().getConfig().setSeperatedPipeHeartbeatEnabled(false);
+
+    final PipeTaskInfo pipeTaskInfo = new PipeTaskInfo();
+    final PipeMeta pipeMeta = createPipeMeta();
+    pipeTaskInfo.createPipe(
+        new CreatePipePlanV2(pipeMeta.getStaticMeta(), 
pipeMeta.getRuntimeMeta()));
+
+    final ParserTestContext context = createParserTestContext(2, pipeTaskInfo);
+    context.parser.parseHeartbeat(1, createPipeHeartbeat(pipeMeta, true));
+    assertEquals(Boolean.TRUE, 
getTemporaryMeta(pipeTaskInfo).getGlobalDegraded());
+
+    context.parser.parseHeartbeat(2, createPipeHeartbeat(pipeMeta, false));
+    assertEquals(Boolean.TRUE, 
getTemporaryMeta(pipeTaskInfo).getGlobalDegraded());
+
+    context.parser.parseHeartbeat(1, createPipeHeartbeat(pipeMeta, false));
+    assertEquals(Boolean.FALSE, 
getTemporaryMeta(pipeTaskInfo).getGlobalDegraded());
+    verify(context.procedureManager, 
never()).pipeHandleMetaChange(anyBoolean(), anyBoolean());
+  }
+
+  @Test
+  public void testParseHeartbeatTreatsMissingPipeDegradedStatusAsUnknown() 
throws Exception {
+    
CommonDescriptor.getInstance().getConfig().setSeperatedPipeHeartbeatEnabled(false);
+
+    final PipeTaskInfo pipeTaskInfo = new PipeTaskInfo();
+    final PipeMeta pipeMeta = createPipeMeta();
+    pipeTaskInfo.createPipe(
+        new CreatePipePlanV2(pipeMeta.getStaticMeta(), 
pipeMeta.getRuntimeMeta()));
+
+    final ParserTestContext context = createParserTestContext(1, pipeTaskInfo);
+    context.parser.parseHeartbeat(
+        1,
+        new PipeHeartbeat(
+            Collections.singletonList(pipeMeta.serialize()),
+            Collections.singletonList(false),
+            Collections.singletonList(0L),
+            Collections.singletonList(0d),
+            null));
+
+    assertNull(getTemporaryMeta(pipeTaskInfo).getGlobalDegraded());
+    verify(context.procedureManager, 
never()).pipeHandleMetaChange(anyBoolean(), anyBoolean());
+  }
+
   private ParserTestContext createParserTestContext(final int 
registeredDataNodeCount) {
+    return createParserTestContext(registeredDataNodeCount, new 
PipeTaskInfo());
+  }
+
+  private ParserTestContext createParserTestContext(
+      final int registeredDataNodeCount, final PipeTaskInfo pipeTaskInfo) {
     final ConfigManager configManager = Mockito.mock(ConfigManager.class);
     final NodeManager nodeManager = Mockito.mock(NodeManager.class);
     final ProcedureManager procedureManager = 
Mockito.mock(ProcedureManager.class);
@@ -134,7 +217,7 @@ public class PipeHeartbeatParserTest {
     
when(pipeManager.getPipeRuntimeCoordinator()).thenReturn(pipeRuntimeCoordinator);
     when(pipeManager.getPipeTaskCoordinator()).thenReturn(pipeTaskCoordinator);
     
when(pipeRuntimeCoordinator.getProcedureSubmitter()).thenReturn(procedureSubmitter);
-    when(pipeTaskCoordinator.tryLock()).thenReturn(new AtomicReference<>(new 
PipeTaskInfo()));
+    when(pipeTaskCoordinator.tryLock()).thenReturn(new 
AtomicReference<>(pipeTaskInfo));
     when(procedureManager.pipeHandleMetaChange(anyBoolean(), 
anyBoolean())).thenReturn(true);
     Mockito.doAnswer(
             invocation -> {
@@ -147,6 +230,21 @@ public class PipeHeartbeatParserTest {
     return new ParserTestContext(new PipeHeartbeatParser(configManager), 
procedureManager);
   }
 
+  private PipeHeartbeat createPipeHeartbeat(final PipeMeta pipeMeta, final 
boolean isDegraded)
+      throws Exception {
+    return new PipeHeartbeat(
+        Collections.singletonList(pipeMeta.serialize()),
+        Collections.singletonList(false),
+        Collections.singletonList(0L),
+        Collections.singletonList(0d),
+        
Collections.singletonList(PipeTemporaryMeta.encodeTsFileEpochDegradedStatus(isDegraded)));
+  }
+
+  private PipeTemporaryMetaInCoordinator getTemporaryMeta(final PipeTaskInfo 
pipeTaskInfo) {
+    return (PipeTemporaryMetaInCoordinator)
+        pipeTaskInfo.getPipeMetaByPipeName("test_pipe").getTemporaryMeta();
+  }
+
   private void setMetaChangeFlags(
       final PipeHeartbeatParser parser,
       final boolean needWriteConsensusOnConfigNodes,
@@ -165,8 +263,18 @@ public class PipeHeartbeatParserTest {
     ((AtomicBoolean) field.get(parser)).set(value);
   }
 
+  private PipeMeta createPipeMeta() {
+    final PipeRuntimeMeta pipeRuntimeMeta = new PipeRuntimeMeta();
+    pipeRuntimeMeta
+        .getConsensusGroupId2TaskMetaMap()
+        .put(1, new PipeTaskMeta(MinimumProgressIndex.INSTANCE, 1));
+    return new PipeMeta(
+        new PipeStaticMeta("test_pipe", 1L, new HashMap<>(), new HashMap<>(), 
new HashMap<>()),
+        pipeRuntimeMeta);
+  }
+
   private PipeHeartbeat emptyHeartbeat() {
-    return new PipeHeartbeat(Collections.emptyList(), null, null, null);
+    return new PipeHeartbeat(Collections.emptyList(), null, null, null, null);
   }
 
   private static class ParserTestContext {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java
index 8a66830fe1c..dfd3ccf4f03 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java
@@ -39,6 +39,8 @@ import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeRuntimeMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMeta;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMetaInAgent;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeType;
 import org.apache.iotdb.commons.pipe.config.PipeConfig;
 import org.apache.iotdb.commons.pipe.config.constant.PipeSinkConstant;
@@ -413,6 +415,7 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
     final List<Boolean> pipeCompletedList = new ArrayList<>();
     final List<Long> pipeRemainingEventCountList = new ArrayList<>();
     final List<Double> pipeRemainingTimeList = new ArrayList<>();
+    final List<Integer> pipeDegradedStatusList = new ArrayList<>();
     try {
       for (final PipeMeta pipeMeta : pipeMetaKeeper.getPipeMetaList()) {
         pipeMetaBinaryList.add(pipeMeta.serialize());
@@ -448,6 +451,10 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
         pipeCompletedList.add(isCompleted);
         pipeRemainingEventCountList.add(remainingEventAndTime.getLeft());
         pipeRemainingTimeList.add(remainingEventAndTime.getRight());
+        pipeDegradedStatusList.add(
+            PipeTemporaryMeta.encodeTsFileEpochDegradedStatus(
+                ((PipeTemporaryMetaInAgent) pipeMeta.getTemporaryMeta())
+                    .getGlobalTsFileEpochDegraded()));
 
         logger.ifPresent(
             l ->
@@ -467,6 +474,7 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
     resp.setPipeCompletedList(pipeCompletedList);
     resp.setPipeRemainingEventCountList(pipeRemainingEventCountList);
     resp.setPipeRemainingTimeList(pipeRemainingTimeList);
+    resp.setPipeDegradedStatusList(pipeDegradedStatusList);
     PipeInsertionDataNodeListener.getInstance().listenToHeartbeat(true);
   }
 
@@ -497,6 +505,7 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
     final List<Boolean> pipeCompletedList = new ArrayList<>();
     final List<Long> pipeRemainingEventCountList = new ArrayList<>();
     final List<Double> pipeRemainingTimeList = new ArrayList<>();
+    final List<Integer> pipeDegradedStatusList = new ArrayList<>();
     try {
       for (final PipeMeta pipeMeta : pipeMetaKeeper.getPipeMetaList()) {
         pipeMetaBinaryList.add(pipeMeta.serialize());
@@ -523,6 +532,10 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
         pipeCompletedList.add(isCompleted);
         pipeRemainingEventCountList.add(remainingEventAndTime.getLeft());
         pipeRemainingTimeList.add(remainingEventAndTime.getRight());
+        pipeDegradedStatusList.add(
+            PipeTemporaryMeta.encodeTsFileEpochDegradedStatus(
+                ((PipeTemporaryMetaInAgent) pipeMeta.getTemporaryMeta())
+                    .getGlobalTsFileEpochDegraded()));
 
         logger.ifPresent(
             l ->
@@ -542,6 +555,7 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
     resp.setPipeCompletedList(pipeCompletedList);
     resp.setPipeRemainingEventCountList(pipeRemainingEventCountList);
     resp.setPipeRemainingTimeList(pipeRemainingTimeList);
+    resp.setPipeDegradedStatusList(pipeDegradedStatusList);
     PipeInsertionDataNodeListener.getInstance().listenToHeartbeat(true);
   }
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/PipeRealtimeDataRegionHybridSource.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/PipeRealtimeDataRegionHybridSource.java
index e3c0a032d88..13263c1a5fa 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/PipeRealtimeDataRegionHybridSource.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/PipeRealtimeDataRegionHybridSource.java
@@ -40,13 +40,21 @@ import 
org.apache.iotdb.pipe.api.event.dml.insertion.TsFileInsertionEvent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Collections;
 import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 public class PipeRealtimeDataRegionHybridSource extends 
PipeRealtimeDataRegionSource {
 
   private static final Logger LOGGER =
       LoggerFactory.getLogger(PipeRealtimeDataRegionHybridSource.class);
 
+  private final Set<TsFileEpoch> activeTsFileEpochs =
+      Collections.newSetFromMap(new ConcurrentHashMap<>());
+  private final Set<TsFileEpoch> degradedTsFileEpochs =
+      Collections.newSetFromMap(new ConcurrentHashMap<>());
+
   @Override
   protected void doExtract(final PipeRealtimeEvent event) {
     final Event eventToExtract = event.getEvent();
@@ -79,6 +87,8 @@ public class PipeRealtimeDataRegionHybridSource extends 
PipeRealtimeDataRegionSo
   }
 
   private void extractTabletInsertion(final PipeRealtimeEvent event) {
+    markTsFileEpochActive(event.getTsFileEpoch());
+
     TsFileEpoch.State state;
 
     if (canNotUseTabletAnymore(event)) {
@@ -105,6 +115,9 @@ public class PipeRealtimeDataRegionHybridSource extends 
PipeRealtimeDataRegionSo
     }
 
     state = event.getTsFileEpoch().getState(this);
+    if (state == TsFileEpoch.State.USING_TSFILE || state == 
TsFileEpoch.State.USING_BOTH) {
+      markTsFileEpochDegraded(event.getTsFileEpoch());
+    }
     switch (state) {
       case USING_TSFILE:
         // Ignore the tablet event.
@@ -129,6 +142,8 @@ public class PipeRealtimeDataRegionHybridSource extends 
PipeRealtimeDataRegionSo
   }
 
   private void extractTsFileInsertion(final PipeRealtimeEvent event) {
+    markTsFileEpochActive(event.getTsFileEpoch());
+
     // Notice that, if the tsFile is partially extracted because the pipe is 
not opened before, the
     // former data won't be extracted
     event
@@ -154,6 +169,9 @@ public class PipeRealtimeDataRegionHybridSource extends 
PipeRealtimeDataRegionSo
             });
 
     final TsFileEpoch.State state = event.getTsFileEpoch().getState(this);
+    if (state == TsFileEpoch.State.USING_BOTH) {
+      markTsFileEpochDegraded(event.getTsFileEpoch());
+    }
     switch (state) {
       case USING_TABLET:
         // If the state is USING_TABLET, discard the event
@@ -161,6 +179,7 @@ public class PipeRealtimeDataRegionHybridSource extends 
PipeRealtimeDataRegionSo
             .eliminateProgressIndex(
                 dataRegionId, getTsFileDedupScopeID(), 
event.getTsFileEpoch().getFilePath());
         
event.decreaseReferenceCount(PipeRealtimeDataRegionHybridSource.class.getName(),
 false);
+        clearTsFileEpoch(event.getTsFileEpoch());
         return;
       case EMPTY:
       case USING_TSFILE:
@@ -175,6 +194,44 @@ public class PipeRealtimeDataRegionHybridSource extends 
PipeRealtimeDataRegionSo
     }
   }
 
+  private void markTsFileEpochActive(final TsFileEpoch tsFileEpoch) {
+    activeTsFileEpochs.add(tsFileEpoch);
+    reportTsFileEpochDegradedStatus();
+  }
+
+  private void markTsFileEpochDegraded(final TsFileEpoch tsFileEpoch) {
+    activeTsFileEpochs.add(tsFileEpoch);
+    degradedTsFileEpochs.add(tsFileEpoch);
+    reportTsFileEpochDegradedStatus();
+  }
+
+  private void clearTsFileEpoch(final TsFileEpoch tsFileEpoch) {
+    activeTsFileEpochs.remove(tsFileEpoch);
+    degradedTsFileEpochs.remove(tsFileEpoch);
+    reportTsFileEpochDegradedStatus();
+  }
+
+  private void reportTsFileEpochDegradedStatus() {
+    if (activeTsFileEpochs.isEmpty()) {
+      PipeDataNodeAgent.task().clearPipeTsFileEpochDegraded(pipeName, 
creationTime, dataRegionId);
+    } else {
+      PipeDataNodeAgent.task()
+          .setPipeTsFileEpochDegraded(
+              pipeName, creationTime, dataRegionId, 
!degradedTsFileEpochs.isEmpty());
+    }
+  }
+
+  @Override
+  public void close() throws Exception {
+    try {
+      super.close();
+    } finally {
+      activeTsFileEpochs.clear();
+      degradedTsFileEpochs.clear();
+      PipeDataNodeAgent.task().clearPipeTsFileEpochDegraded(pipeName, 
creationTime, dataRegionId);
+    }
+  }
+
   // If the insertNode's memory has reached the dangerous threshold, we should 
not extract any
   // tablets.
   private boolean canNotUseTabletAnymore(final PipeRealtimeEvent event) {
@@ -262,6 +319,7 @@ public class PipeRealtimeDataRegionHybridSource extends 
PipeRealtimeDataRegionSo
       // this event is not reliable anymore. but the data represented by this 
event
       // has been carried by the following tsfile event, so we can just 
discard this event.
       event.getTsFileEpoch().migrateState(this, s -> 
TsFileEpoch.State.USING_BOTH);
+      markTsFileEpochDegraded(event.getTsFileEpoch());
       LOGGER.warn(
           "Discard tablet event {} because it is not reliable anymore. "
               + "Change the state of TsFileEpoch to USING_BOTH.",
@@ -271,23 +329,27 @@ public class PipeRealtimeDataRegionHybridSource extends 
PipeRealtimeDataRegionSo
   }
 
   private Event supplyTsFileInsertion(final PipeRealtimeEvent event) {
-    if 
(event.increaseReferenceCount(PipeRealtimeDataRegionHybridSource.class.getName()))
 {
-      return event.getEvent();
-    } else {
-      // If the event's reference count can not be increased, it means the 
data represented by
-      // this event is not reliable anymore. the data has been lost. we simply 
discard this
-      // event and report the exception to PipeRuntimeAgent.
-      final String errorMessage =
-          String.format(
-              
DataNodePipeMessages.EVENT_CAN_NOT_BE_SUPPLIED_BECAUSE_DATA_IS_LOST,
-              event.getEvent());
-      LOGGER.error(errorMessage);
-      PipeDataNodeAgent.runtime()
-          .report(pipeTaskMeta, new 
PipeRuntimeNonCriticalException(errorMessage));
-      PipeTsFileEpochProgressIndexKeeper.getInstance()
-          .eliminateProgressIndex(
-              dataRegionId, getTsFileDedupScopeID(), 
event.getTsFileEpoch().getFilePath());
-      return null;
+    try {
+      if 
(event.increaseReferenceCount(PipeRealtimeDataRegionHybridSource.class.getName()))
 {
+        return event.getEvent();
+      } else {
+        // If the event's reference count can not be increased, it means the 
data represented by
+        // this event is not reliable anymore. the data has been lost. we 
simply discard this
+        // event and report the exception to PipeRuntimeAgent.
+        final String errorMessage =
+            String.format(
+                
DataNodePipeMessages.EVENT_CAN_NOT_BE_SUPPLIED_BECAUSE_DATA_IS_LOST,
+                event.getEvent());
+        LOGGER.error(errorMessage);
+        PipeDataNodeAgent.runtime()
+            .report(pipeTaskMeta, new 
PipeRuntimeNonCriticalException(errorMessage));
+        PipeTsFileEpochProgressIndexKeeper.getInstance()
+            .eliminateProgressIndex(
+                dataRegionId, getTsFileDedupScopeID(), 
event.getTsFileEpoch().getFilePath());
+        return null;
+      }
+    } finally {
+      clearTsFileEpoch(event.getTsFileEpoch());
     }
   }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java
index a501824850d..7ca14b38284 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java
@@ -704,6 +704,11 @@ public class InformationSchemaContentSupplierFactory {
 
       columnBuilders[7].writeLong(tPipeInfo.isSetRemainingEventCount() ? 
remainingEventCount : -1);
       columnBuilders[8].writeDouble(tPipeInfo.isSetEstimatedRemainingTime() ? 
remainingTime : -1);
+      if (tPipeInfo.isSetIsDegraded()) {
+        columnBuilders[9].writeBoolean(tPipeInfo.isIsDegraded());
+      } else {
+        columnBuilders[9].appendNull();
+      }
 
       resultBuilder.declarePosition();
     }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTask.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTask.java
index d204cd04802..6698ae88fdc 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTask.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTask.java
@@ -130,6 +130,11 @@ public class ShowPipeTask implements IConfigTask {
                       ? String.format("%.2f", remainingTime)
                       : "Unknown",
                   TSFileConfig.STRING_CHARSET));
+      if (tPipeInfo.isSetIsDegraded()) {
+        builder.getColumnBuilder(9).writeBoolean(tPipeInfo.isIsDegraded());
+      } else {
+        builder.getColumnBuilder(9).appendNull();
+      }
       builder.declarePosition();
     }
     final DatasetHeader datasetHeader = 
DatasetHeaderFactory.getShowPipeHeader();
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/PipeRealtimeExtractTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/PipeRealtimeExtractTest.java
index b793e8ca699..1b714a80009 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/PipeRealtimeExtractTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/PipeRealtimeExtractTest.java
@@ -22,14 +22,23 @@ package org.apache.iotdb.db.pipe.source;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.commons.consensus.index.impl.MinimumProgressIndex;
 import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.commons.pipe.agent.task.PipeTaskAgent;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMetaKeeper;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeRuntimeMeta;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta;
+import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMetaInAgent;
 import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant;
 import 
org.apache.iotdb.commons.pipe.config.plugin.configuraion.PipeTaskRuntimeConfiguration;
 import 
org.apache.iotdb.commons.pipe.config.plugin.env.PipeTaskSourceRuntimeEnvironment;
+import org.apache.iotdb.commons.pipe.event.EnrichedEvent;
 import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.commons.utils.FileUtils;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent;
 import org.apache.iotdb.db.pipe.event.realtime.PipeRealtimeEvent;
+import org.apache.iotdb.db.pipe.event.realtime.PipeRealtimeEventFactory;
 import 
org.apache.iotdb.db.pipe.source.dataregion.realtime.PipeRealtimeDataRegionHybridSource;
 import 
org.apache.iotdb.db.pipe.source.dataregion.realtime.PipeRealtimeDataRegionLogSource;
 import 
org.apache.iotdb.db.pipe.source.dataregion.realtime.PipeRealtimeDataRegionSource;
@@ -55,6 +64,7 @@ import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.nio.file.Files;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -72,6 +82,9 @@ import java.util.function.Function;
 public class PipeRealtimeExtractTest {
 
   private static final Logger LOGGER = 
LoggerFactory.getLogger(PipeRealtimeExtractTest.class);
+  private static final String TEST_PIPE_NAME = "test_degraded_status_pipe";
+  private static final long TEST_PIPE_CREATION_TIME = 1L;
+  private static final String TEST_REFERENCE_HOLDER = 
PipeRealtimeExtractTest.class.getName();
 
   private final int dataRegion1 = 1;
   private final int dataRegion2 = 2;
@@ -87,9 +100,10 @@ public class PipeRealtimeExtractTest {
   private int dataNodeId;
 
   @Before
-  public void setUp() throws IOException {
+  public void setUp() throws Exception {
     dataNodeId = IoTDBDescriptor.getInstance().getConfig().getDataNodeId();
     IoTDBDescriptor.getInstance().getConfig().setDataNodeId(0);
+    removeTestPipeMeta();
     writeService = Executors.newFixedThreadPool(2);
     listenerService = Executors.newFixedThreadPool(4);
     tmpDir = new 
File(Files.createTempDirectory("pipeRealtimeExtractor").toString());
@@ -103,11 +117,12 @@ public class PipeRealtimeExtractTest {
   }
 
   @After
-  public void tearDown() {
+  public void tearDown() throws Exception {
     IoTDBDescriptor.getInstance().getConfig().setDataNodeId(dataNodeId);
     writeService.shutdownNow();
     listenerService.shutdownNow();
     FileUtils.deleteFileOrDirectory(tmpDir);
+    removeTestPipeMeta();
   }
 
   @Test
@@ -310,6 +325,59 @@ public class PipeRealtimeExtractTest {
     }
   }
 
+  @Test
+  public void testHybridSourceReportsTsFileEpochDegradedStatus() throws 
Exception {
+    registerTestPipeMeta();
+
+    try (final PipeRealtimeDataRegionHybridSource extractor =
+        new PipeRealtimeDataRegionHybridSource()) {
+      final PipeParameters parameters =
+          new PipeParameters(
+              new HashMap<String, String>() {
+                {
+                  put(PipeSourceConstant.EXTRACTOR_PATTERN_KEY, pattern1);
+                }
+              });
+      final PipeTaskRuntimeConfiguration configuration =
+          new PipeTaskRuntimeConfiguration(
+              new PipeTaskSourceRuntimeEnvironment(
+                  TEST_PIPE_NAME,
+                  TEST_PIPE_CREATION_TIME,
+                  dataRegion1,
+                  new PipeTaskMeta(MinimumProgressIndex.INSTANCE, 1)));
+
+      extractor.validate(new PipeParameterValidator(parameters));
+      extractor.customize(parameters, configuration);
+
+      final TsFileResource resource = createTsFileResource(dataRegion1, 
"100-100-0-0.tsfile");
+      final PipeRealtimeEvent tabletEvent =
+          PipeRealtimeEventFactory.createRealtimeEvent(
+              false, "root.sg", createInsertRowNode("degraded-tablet", "a"), 
resource);
+
+      
Assert.assertTrue(tabletEvent.increaseReferenceCount(TEST_REFERENCE_HOLDER));
+      extractor.extract(tabletEvent);
+      Assert.assertEquals(Boolean.FALSE, getGlobalTsFileEpochDegraded());
+
+      tabletEvent.clearReferenceCount(TEST_REFERENCE_HOLDER);
+      Assert.assertNull(extractor.supply());
+      Assert.assertEquals(Boolean.TRUE, getGlobalTsFileEpochDegraded());
+
+      final PipeRealtimeEvent tsFileEvent =
+          PipeRealtimeEventFactory.createRealtimeEvent(false, "root.sg", 
resource, false);
+
+      
Assert.assertTrue(tsFileEvent.increaseReferenceCount(TEST_REFERENCE_HOLDER));
+      extractor.extract(tsFileEvent);
+      Assert.assertEquals(Boolean.TRUE, getGlobalTsFileEpochDegraded());
+
+      final Event suppliedEvent = extractor.supply();
+      Assert.assertTrue(suppliedEvent instanceof TsFileInsertionEvent);
+      releaseSuppliedEvent(suppliedEvent);
+      Assert.assertNull(getGlobalTsFileEpochDegraded());
+    }
+
+    Assert.assertNull(getGlobalTsFileEpochDegraded());
+  }
+
   private Future<?> write2DataRegion(
       final int writeNum, final int dataRegionId, final int startNum) {
     final File dataRegionDir =
@@ -398,6 +466,85 @@ public class PipeRealtimeExtractTest {
         });
   }
 
+  private TsFileResource createTsFileResource(final int dataRegionId, final 
String fileName)
+      throws IOException {
+    final File dataRegionDir =
+        new File(tsFileDir.getPath() + File.separator + dataRegionId + 
File.separator + "0");
+    Assert.assertTrue(dataRegionDir.mkdirs() || dataRegionDir.isDirectory());
+
+    final File tsFile = new File(dataRegionDir, fileName);
+    Assert.assertTrue(tsFile.createNewFile());
+
+    final TsFileResource resource = new TsFileResource(tsFile);
+    resource.updateStartTime(
+        IDeviceID.Factory.DEFAULT_FACTORY.create(
+            String.join(TsFileConstant.PATH_SEPARATOR, device)),
+        0);
+    resource.close();
+    return resource;
+  }
+
+  private InsertRowNode createInsertRowNode(final String planNodeId, final 
String measurement)
+      throws Exception {
+    return new InsertRowNode(
+        new PlanNodeId(planNodeId),
+        new PartialPath(device),
+        false,
+        new String[] {measurement},
+        new TSDataType[] {TSDataType.INT32},
+        0,
+        new Integer[] {1},
+        false);
+  }
+
+  private void registerTestPipeMeta() throws Exception {
+    final PipeMetaKeeper pipeMetaKeeper = getPipeMetaKeeper();
+    pipeMetaKeeper.acquireWriteLock();
+    try {
+      pipeMetaKeeper.removePipeMeta(TEST_PIPE_NAME);
+      pipeMetaKeeper.addPipeMeta(
+          new PipeMeta(
+                  new PipeStaticMeta(
+                      TEST_PIPE_NAME,
+                      TEST_PIPE_CREATION_TIME,
+                      new HashMap<>(),
+                      new HashMap<>(),
+                      new HashMap<>()),
+                  new PipeRuntimeMeta())
+              .deepCopy4TaskAgent());
+    } finally {
+      pipeMetaKeeper.releaseWriteLock();
+    }
+  }
+
+  private void removeTestPipeMeta() throws Exception {
+    final PipeMetaKeeper pipeMetaKeeper = getPipeMetaKeeper();
+    pipeMetaKeeper.acquireWriteLock();
+    try {
+      pipeMetaKeeper.removePipeMeta(TEST_PIPE_NAME);
+    } finally {
+      pipeMetaKeeper.releaseWriteLock();
+    }
+  }
+
+  private Boolean getGlobalTsFileEpochDegraded() throws Exception {
+    final PipeMeta pipeMeta = getPipeMetaKeeper().getPipeMeta(TEST_PIPE_NAME);
+    Assert.assertNotNull(pipeMeta);
+    return ((PipeTemporaryMetaInAgent) 
pipeMeta.getTemporaryMeta()).getGlobalTsFileEpochDegraded();
+  }
+
+  private PipeMetaKeeper getPipeMetaKeeper() throws Exception {
+    final Field pipeMetaKeeperField = 
PipeTaskAgent.class.getDeclaredField("pipeMetaKeeper");
+    pipeMetaKeeperField.setAccessible(true);
+    return (PipeMetaKeeper) pipeMetaKeeperField.get(PipeDataNodeAgent.task());
+  }
+
+  private void releaseSuppliedEvent(final Event event) {
+    if (event instanceof EnrichedEvent) {
+      ((EnrichedEvent) event).clearReferenceCount(TEST_REFERENCE_HOLDER);
+    }
+  }
+
   private static class NoTsFileRealtimeDataRegionSource extends 
PipeRealtimeDataRegionSource {
 
     private final AtomicInteger observedTsFileEventCount = new 
AtomicInteger(0);
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTaskTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTaskTest.java
new file mode 100644
index 00000000000..3f89e8ee9cc
--- /dev/null
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/pipe/ShowPipeTaskTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.queryengine.plan.execution.config.sys.pipe;
+
+import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant;
+import org.apache.iotdb.confignode.rpc.thrift.TShowPipeInfo;
+import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import com.google.common.util.concurrent.SettableFuture;
+import org.apache.tsfile.read.common.block.TsBlock;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class ShowPipeTaskTest {
+
+  @Test
+  public void testBuildTSBlockWritesDegradedColumn() throws Exception {
+    final TShowPipeInfo degradedPipe = createPipeInfo("degraded_pipe");
+    degradedPipe.setIsDegraded(true);
+    final TShowPipeInfo normalPipe = createPipeInfo("normal_pipe");
+    normalPipe.setIsDegraded(false);
+    final TShowPipeInfo unknownPipe = createPipeInfo("unknown_pipe");
+
+    final SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+    ShowPipeTask.buildTSBlock(Arrays.asList(degradedPipe, normalPipe, 
unknownPipe), future);
+
+    final ConfigTaskResult result = future.get();
+    final TsBlock resultSet = result.getResultSet();
+
+    assertEquals(TSStatusCode.SUCCESS_STATUS, result.getStatusCode());
+    assertEquals(
+        ColumnHeaderConstant.IS_DEGRADED, 
result.getResultSetHeader().getRespColumns().get(9));
+    assertEquals(3, resultSet.getPositionCount());
+    assertTrue(resultSet.getColumn(9).getBoolean(0));
+    assertFalse(resultSet.getColumn(9).getBoolean(1));
+    assertTrue(resultSet.getColumn(9).isNull(2));
+  }
+
+  private TShowPipeInfo createPipeInfo(final String pipeName) {
+    return new TShowPipeInfo(
+        pipeName,
+        1L,
+        "RUNNING",
+        "{source=iotdb-source}",
+        "{processor=do-nothing-processor}",
+        "{sink=iotdb-thrift-sink}",
+        "");
+  }
+}
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java
index 94904092cc4..b404a2ce00b 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java
@@ -1176,6 +1176,28 @@ public abstract class PipeTaskAgent {
     }
   }
 
+  public void setPipeTsFileEpochDegraded(
+      final String pipeName,
+      final long creationTime,
+      final int regionId,
+      final boolean isDegraded) {
+    final PipeMeta pipeMeta = pipeMetaKeeper.getPipeMeta(pipeName);
+    // To avoid stale pipe before alter
+    if (Objects.nonNull(pipeMeta) && 
pipeMeta.getStaticMeta().getCreationTime() == creationTime) {
+      ((PipeTemporaryMetaInAgent) pipeMeta.getTemporaryMeta())
+          .setTsFileEpochDegraded(regionId, isDegraded);
+    }
+  }
+
+  public void clearPipeTsFileEpochDegraded(
+      final String pipeName, final long creationTime, final int regionId) {
+    final PipeMeta pipeMeta = pipeMetaKeeper.getPipeMeta(pipeName);
+    // To avoid stale pipe before alter
+    if (Objects.nonNull(pipeMeta) && 
pipeMeta.getStaticMeta().getCreationTime() == creationTime) {
+      ((PipeTemporaryMetaInAgent) 
pipeMeta.getTemporaryMeta()).clearTsFileEpochDegraded(regionId);
+    }
+  }
+
   public int getPipeCount() {
     return pipeMetaKeeper.getPipeMetaCount();
   }
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMeta.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMeta.java
index 5363f47190b..d839924aacb 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMeta.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMeta.java
@@ -19,4 +19,31 @@
 
 package org.apache.iotdb.commons.pipe.agent.task.meta;
 
-public interface PipeTemporaryMeta {}
+public interface PipeTemporaryMeta {
+
+  int TS_FILE_EPOCH_DEGRADED_STATUS_UNKNOWN = -1;
+  int TS_FILE_EPOCH_DEGRADED_STATUS_FALSE = 0;
+  int TS_FILE_EPOCH_DEGRADED_STATUS_TRUE = 1;
+
+  static int encodeTsFileEpochDegradedStatus(final Boolean isDegraded) {
+    if (isDegraded == null) {
+      return TS_FILE_EPOCH_DEGRADED_STATUS_UNKNOWN;
+    }
+    return isDegraded ? TS_FILE_EPOCH_DEGRADED_STATUS_TRUE : 
TS_FILE_EPOCH_DEGRADED_STATUS_FALSE;
+  }
+
+  static Boolean decodeTsFileEpochDegradedStatus(final Integer status) {
+    if (status == null) {
+      return null;
+    }
+    switch (status) {
+      case TS_FILE_EPOCH_DEGRADED_STATUS_FALSE:
+        return false;
+      case TS_FILE_EPOCH_DEGRADED_STATUS_TRUE:
+        return true;
+      case TS_FILE_EPOCH_DEGRADED_STATUS_UNKNOWN:
+      default:
+        return null;
+    }
+  }
+}
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInAgent.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInAgent.java
index 23914fa8d84..c28bb51236d 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInAgent.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInAgent.java
@@ -24,12 +24,15 @@ import 
org.apache.iotdb.commons.pipe.agent.task.progress.CommitterKey;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicLong;
 
 public class PipeTemporaryMetaInAgent implements PipeTemporaryMeta {
 
   // Statistics
   private final AtomicLong floatingMemoryUsageInByte = new AtomicLong(0L);
+  private final ConcurrentMap<Integer, Boolean> 
regionId2TsFileEpochDegradedMap =
+      new ConcurrentHashMap<>();
 
   // Object pool
   private final String pipeNameWithCreationTime;
@@ -53,6 +56,21 @@ public class PipeTemporaryMetaInAgent implements 
PipeTemporaryMeta {
     return floatingMemoryUsageInByte.get();
   }
 
+  public void setTsFileEpochDegraded(final int regionId, final boolean 
isDegraded) {
+    regionId2TsFileEpochDegradedMap.put(regionId, isDegraded);
+  }
+
+  public void clearTsFileEpochDegraded(final int regionId) {
+    regionId2TsFileEpochDegradedMap.remove(regionId);
+  }
+
+  public Boolean getGlobalTsFileEpochDegraded() {
+    if 
(regionId2TsFileEpochDegradedMap.values().stream().anyMatch(Boolean.TRUE::equals))
 {
+      return true;
+    }
+    return regionId2TsFileEpochDegradedMap.isEmpty() ? null : false;
+  }
+
   public String getPipeNameWithCreationTime() {
     return pipeNameWithCreationTime;
   }
@@ -87,12 +105,15 @@ public class PipeTemporaryMetaInAgent implements 
PipeTemporaryMeta {
     final PipeTemporaryMetaInAgent that = (PipeTemporaryMetaInAgent) o;
     return Objects.equals(
             this.floatingMemoryUsageInByte.get(), 
that.floatingMemoryUsageInByte.get())
+        && Objects.equals(
+            this.regionId2TsFileEpochDegradedMap, 
that.regionId2TsFileEpochDegradedMap)
         && Objects.equals(this.regionId2CommitterKeyMap, 
that.regionId2CommitterKeyMap);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(floatingMemoryUsageInByte, regionId2CommitterKeyMap);
+    return Objects.hash(
+        floatingMemoryUsageInByte.get(), regionId2TsFileEpochDegradedMap, 
regionId2CommitterKeyMap);
   }
 
   @Override
@@ -100,6 +121,8 @@ public class PipeTemporaryMetaInAgent implements 
PipeTemporaryMeta {
     return "PipeTemporaryMeta{"
         + "floatingMemoryUsage="
         + floatingMemoryUsageInByte
+        + ", regionId2TsFileEpochDegradedMap="
+        + regionId2TsFileEpochDegradedMap
         + ", regionId2CommitterKeyMap="
         + regionId2CommitterKeyMap
         + '}';
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInCoordinator.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInCoordinator.java
index ee127bbae44..b44649ff30c 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInCoordinator.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaInCoordinator.java
@@ -32,6 +32,7 @@ public class PipeTemporaryMetaInCoordinator implements 
PipeTemporaryMeta {
       Collections.newSetFromMap(new ConcurrentHashMap<>());
   private final ConcurrentMap<Integer, Long> nodeId2RemainingEventMap = new 
ConcurrentHashMap<>();
   private final ConcurrentMap<Integer, Double> nodeId2RemainingTimeMap = new 
ConcurrentHashMap<>();
+  private final ConcurrentMap<Integer, Boolean> nodeId2IsDegradedMap = new 
ConcurrentHashMap<>();
 
   public void markDataNodeCompleted(final int dataNodeId) {
     completedDataNodeIds.add(dataNodeId);
@@ -49,6 +50,14 @@ public class PipeTemporaryMetaInCoordinator implements 
PipeTemporaryMeta {
     nodeId2RemainingTimeMap.put(dataNodeId, remainingTime);
   }
 
+  public void setDegraded(final int dataNodeId, final Boolean isDegraded) {
+    if (Objects.isNull(isDegraded)) {
+      nodeId2IsDegradedMap.remove(dataNodeId);
+    } else {
+      nodeId2IsDegradedMap.put(dataNodeId, isDegraded);
+    }
+  }
+
   public Set<Integer> getCompletedDataNodeIds() {
     return completedDataNodeIds;
   }
@@ -61,6 +70,13 @@ public class PipeTemporaryMetaInCoordinator implements 
PipeTemporaryMeta {
     return 
nodeId2RemainingTimeMap.values().stream().reduce(Math::max).orElse(0d);
   }
 
+  public Boolean getGlobalDegraded() {
+    if (nodeId2IsDegradedMap.values().stream().anyMatch(Boolean.TRUE::equals)) 
{
+      return true;
+    }
+    return nodeId2IsDegradedMap.isEmpty() ? null : false;
+  }
+
   @Override
   public boolean equals(final Object o) {
     if (this == o) {
@@ -72,12 +88,17 @@ public class PipeTemporaryMetaInCoordinator implements 
PipeTemporaryMeta {
     final PipeTemporaryMetaInCoordinator that = 
(PipeTemporaryMetaInCoordinator) o;
     return Objects.equals(this.completedDataNodeIds, that.completedDataNodeIds)
         && Objects.equals(this.nodeId2RemainingEventMap, 
that.nodeId2RemainingEventMap)
-        && Objects.equals(this.nodeId2RemainingTimeMap, 
that.nodeId2RemainingTimeMap);
+        && Objects.equals(this.nodeId2RemainingTimeMap, 
that.nodeId2RemainingTimeMap)
+        && Objects.equals(this.nodeId2IsDegradedMap, 
that.nodeId2IsDegradedMap);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(completedDataNodeIds, nodeId2RemainingEventMap, 
nodeId2RemainingTimeMap);
+    return Objects.hash(
+        completedDataNodeIds,
+        nodeId2RemainingEventMap,
+        nodeId2RemainingTimeMap,
+        nodeId2IsDegradedMap);
   }
 
   @Override
@@ -87,8 +108,10 @@ public class PipeTemporaryMetaInCoordinator implements 
PipeTemporaryMeta {
         + completedDataNodeIds
         + ", nodeId2RemainingEventMap="
         + nodeId2RemainingEventMap
-        + ", nodeId2RemainingTimeMap"
+        + ", nodeId2RemainingTimeMap="
         + nodeId2RemainingTimeMap
+        + ", nodeId2IsDegradedMap="
+        + nodeId2IsDegradedMap
         + '}';
   }
 }
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
index d9d6518ac0b..a1c8b6ddd20 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
@@ -196,6 +196,7 @@ public class ColumnHeaderConstant {
   public static final String EXCEPTION_MESSAGE = "ExceptionMessage";
   public static final String REMAINING_EVENT_COUNT = "RemainingEventCount";
   public static final String ESTIMATED_REMAINING_SECONDS = 
"EstimatedRemainingSeconds";
+  public static final String IS_DEGRADED = "IsDegraded";
 
   // column names for select into
   public static final String SOURCE_DEVICE = "SourceDevice";
@@ -272,6 +273,7 @@ public class ColumnHeaderConstant {
   public static final String REMAINING_EVENT_COUNT_TABLE_MODEL = 
"remaining_event_count";
   public static final String ESTIMATED_REMAINING_SECONDS_TABLE_MODEL =
       "estimated_remaining_seconds";
+  public static final String IS_DEGRADED_TABLE_MODEL = "is_degraded";
 
   public static final String PLUGIN_NAME_TABLE_MODEL = "plugin_name";
   public static final String PLUGIN_TYPE_TABLE_MODEL = "plugin_type";
@@ -608,7 +610,8 @@ public class ColumnHeaderConstant {
           new ColumnHeader(PIPE_CONNECTOR, TSDataType.TEXT),
           new ColumnHeader(EXCEPTION_MESSAGE, TSDataType.TEXT),
           new ColumnHeader(REMAINING_EVENT_COUNT, TSDataType.TEXT),
-          new ColumnHeader(ESTIMATED_REMAINING_SECONDS, TSDataType.TEXT));
+          new ColumnHeader(ESTIMATED_REMAINING_SECONDS, TSDataType.TEXT),
+          new ColumnHeader(IS_DEGRADED, TSDataType.BOOLEAN));
 
   public static final List<ColumnHeader> showTopicColumnHeaders =
       ImmutableList.of(
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java
index 27a1dbdd9d2..32f51a173be 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/InformationSchema.java
@@ -225,6 +225,9 @@ public class InformationSchema {
     pipeTable.addColumnSchema(
         new AttributeColumnSchema(
             ColumnHeaderConstant.ESTIMATED_REMAINING_SECONDS_TABLE_MODEL, 
TSDataType.DOUBLE));
+    pipeTable.addColumnSchema(
+        new AttributeColumnSchema(
+            ColumnHeaderConstant.IS_DEGRADED_TABLE_MODEL, TSDataType.BOOLEAN));
     schemaTables.put(PIPES, pipeTable);
 
     final TsTable pipePluginTable = new TsTable(PIPE_PLUGINS);
diff --git 
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaTest.java
 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaTest.java
new file mode 100644
index 00000000000..b6091ab76f7
--- /dev/null
+++ 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/agent/task/meta/PipeTemporaryMetaTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.commons.pipe.agent.task.meta;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PipeTemporaryMetaTest {
+
+  @Test
+  public void testTsFileEpochDegradedStatusCodec() {
+    Assert.assertEquals(
+        PipeTemporaryMeta.TS_FILE_EPOCH_DEGRADED_STATUS_UNKNOWN,
+        PipeTemporaryMeta.encodeTsFileEpochDegradedStatus(null));
+    Assert.assertEquals(
+        PipeTemporaryMeta.TS_FILE_EPOCH_DEGRADED_STATUS_FALSE,
+        PipeTemporaryMeta.encodeTsFileEpochDegradedStatus(false));
+    Assert.assertEquals(
+        PipeTemporaryMeta.TS_FILE_EPOCH_DEGRADED_STATUS_TRUE,
+        PipeTemporaryMeta.encodeTsFileEpochDegradedStatus(true));
+
+    Assert.assertNull(PipeTemporaryMeta.decodeTsFileEpochDegradedStatus(null));
+    Assert.assertNull(
+        PipeTemporaryMeta.decodeTsFileEpochDegradedStatus(
+            PipeTemporaryMeta.TS_FILE_EPOCH_DEGRADED_STATUS_UNKNOWN));
+    Assert.assertEquals(
+        Boolean.FALSE,
+        PipeTemporaryMeta.decodeTsFileEpochDegradedStatus(
+            PipeTemporaryMeta.TS_FILE_EPOCH_DEGRADED_STATUS_FALSE));
+    Assert.assertEquals(
+        Boolean.TRUE,
+        PipeTemporaryMeta.decodeTsFileEpochDegradedStatus(
+            PipeTemporaryMeta.TS_FILE_EPOCH_DEGRADED_STATUS_TRUE));
+  }
+
+  @Test
+  public void testAgentAggregatesTsFileEpochDegradedStatus() {
+    final PipeTemporaryMetaInAgent temporaryMeta = new 
PipeTemporaryMetaInAgent("test_pipe", 1L);
+
+    Assert.assertNull(temporaryMeta.getGlobalTsFileEpochDegraded());
+
+    temporaryMeta.setTsFileEpochDegraded(1, false);
+    temporaryMeta.setTsFileEpochDegraded(2, false);
+    Assert.assertEquals(Boolean.FALSE, 
temporaryMeta.getGlobalTsFileEpochDegraded());
+
+    temporaryMeta.setTsFileEpochDegraded(2, true);
+    Assert.assertEquals(Boolean.TRUE, 
temporaryMeta.getGlobalTsFileEpochDegraded());
+
+    temporaryMeta.clearTsFileEpochDegraded(2);
+    Assert.assertEquals(Boolean.FALSE, 
temporaryMeta.getGlobalTsFileEpochDegraded());
+
+    temporaryMeta.clearTsFileEpochDegraded(1);
+    Assert.assertNull(temporaryMeta.getGlobalTsFileEpochDegraded());
+  }
+
+  @Test
+  public void testCoordinatorAggregatesNullableDegradedStatus() {
+    final PipeTemporaryMetaInCoordinator temporaryMeta = new 
PipeTemporaryMetaInCoordinator();
+
+    Assert.assertNull(temporaryMeta.getGlobalDegraded());
+
+    temporaryMeta.setDegraded(1, false);
+    temporaryMeta.setDegraded(2, false);
+    Assert.assertEquals(Boolean.FALSE, temporaryMeta.getGlobalDegraded());
+
+    temporaryMeta.setDegraded(2, true);
+    Assert.assertEquals(Boolean.TRUE, temporaryMeta.getGlobalDegraded());
+
+    temporaryMeta.setDegraded(2, null);
+    Assert.assertEquals(Boolean.FALSE, temporaryMeta.getGlobalDegraded());
+
+    temporaryMeta.setDegraded(1, null);
+    Assert.assertNull(temporaryMeta.getGlobalDegraded());
+  }
+}
diff --git a/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift 
b/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift
index a107b0259ff..d52dc08813f 100644
--- a/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift
+++ b/iotdb-protocol/thrift-commons/src/main/thrift/common.thrift
@@ -202,6 +202,7 @@ struct TPipeHeartbeatResp {
   2: optional list<bool> pipeCompletedList
   3: optional list<i64> pipeRemainingEventCountList
   4: optional list<double> pipeRemainingTimeList
+  5: optional list<i32> pipeDegradedStatusList
 }
 
 struct TLicense {
diff --git a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift 
b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift
index 852460fb60e..955aa6fe39a 100644
--- a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift
+++ b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift
@@ -869,6 +869,7 @@ struct TShowPipeInfo {
   7: required string exceptionMessage
   8: optional i64 remainingEventCount
   9: optional double EstimatedRemainingTime
+  10: optional bool isDegraded
 }
 
 struct TGetAllPipeInfoResp {
diff --git a/iotdb-protocol/thrift-datanode/src/main/thrift/datanode.thrift 
b/iotdb-protocol/thrift-datanode/src/main/thrift/datanode.thrift
index 8316aa0ec5b..b74895260fa 100644
--- a/iotdb-protocol/thrift-datanode/src/main/thrift/datanode.thrift
+++ b/iotdb-protocol/thrift-datanode/src/main/thrift/datanode.thrift
@@ -316,6 +316,7 @@ struct TDataNodeHeartbeatResp {
   15: optional list<i64> pipeRemainingEventCountList
   16: optional list<double> pipeRemainingTimeList
   17: optional map<i32, i64> dataRegionRawDataSize
+  18: optional list<i32> pipeDegradedStatusList
 }
 
 struct TPipeHeartbeatReq {

Reply via email to