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

jt2594838 pushed a commit to branch dev/1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/dev/1.3 by this push:
     new ed87f2d0e83 [To dev/1.3] [Pipe] Preserve sink exception root causes 
(#18335) (#18342)
ed87f2d0e83 is described below

commit ed87f2d0e83c90b8a364d367be05f45000de86b2
Author: Caideyipi <[email protected]>
AuthorDate: Thu Jul 30 10:44:12 2026 +0800

    [To dev/1.3] [Pipe] Preserve sink exception root causes (#18335) (#18342)
    
    * Preserve Pipe sink exception root causes (#18335)
    
    * Fix PipeSinkSubtaskTest compilation
---
 .../agent/task/subtask/sink/PipeSinkSubtask.java   |  2 +-
 .../task/subtask/sink/PipeSinkSubtaskTest.java     | 92 ++++++++++++++++++++++
 .../pipe/PipeRuntimeCriticalException.java         |  4 +
 .../exception/pipe/PipeRuntimeException.java       |  4 +
 .../pipe/PipeRuntimeSinkCriticalException.java     |  4 +
 .../task/subtask/PipeAbstractSinkSubtask.java      |  9 ++-
 6 files changed, 111 insertions(+), 4 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/sink/PipeSinkSubtask.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/sink/PipeSinkSubtask.java
index eed4b729ed2..91a588ce699 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/sink/PipeSinkSubtask.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/sink/PipeSinkSubtask.java
@@ -504,7 +504,7 @@ public class PipeSinkSubtask extends 
PipeAbstractSinkSubtask {
 
   @Override
   protected String getRootCause(final Throwable throwable) {
-    return ErrorHandlingCommonUtils.getRootCause(throwable).getMessage();
+    return ErrorHandlingCommonUtils.getRootCause(throwable).toString();
   }
 
   @Override
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/subtask/sink/PipeSinkSubtaskTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/subtask/sink/PipeSinkSubtaskTest.java
index 6f14de41471..8960e7fea96 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/subtask/sink/PipeSinkSubtaskTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/subtask/sink/PipeSinkSubtaskTest.java
@@ -20,10 +20,14 @@
 package org.apache.iotdb.db.pipe.agent.task.subtask.sink;
 
 import org.apache.iotdb.commons.conf.CommonDescriptor;
+import org.apache.iotdb.commons.exception.pipe.PipeRuntimeException;
+import 
org.apache.iotdb.commons.exception.pipe.PipeRuntimeSinkCriticalException;
 import 
org.apache.iotdb.commons.exception.pipe.PipeRuntimeSinkNonReportTimeConfigurableException;
 import 
org.apache.iotdb.commons.pipe.agent.task.connection.UnboundedBlockingPendingQueue;
 import org.apache.iotdb.commons.pipe.agent.task.progress.CommitterKey;
+import org.apache.iotdb.commons.pipe.event.EnrichedEvent;
 import 
org.apache.iotdb.commons.pipe.sink.protocol.PipeConnectorWithEventDiscard;
+import org.apache.iotdb.commons.utils.ErrorHandlingCommonUtils;
 import org.apache.iotdb.db.pipe.event.common.heartbeat.PipeHeartbeatEvent;
 import org.apache.iotdb.pipe.api.PipeConnector;
 import 
org.apache.iotdb.pipe.api.customizer.configuration.PipeConnectorRuntimeConfiguration;
@@ -32,6 +36,7 @@ import 
org.apache.iotdb.pipe.api.customizer.parameter.PipeParameters;
 import org.apache.iotdb.pipe.api.event.Event;
 import org.apache.iotdb.pipe.api.event.dml.insertion.TabletInsertionEvent;
 import org.apache.iotdb.pipe.api.exception.PipeConnectionException;
+import org.apache.iotdb.pipe.api.exception.PipeException;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -227,6 +232,62 @@ public class PipeSinkSubtaskTest {
     }
   }
 
+  @Test
+  public void 
testTransferExceptionWithNullRootCauseMessageIncludesExceptionType()
+      throws Exception {
+    final PipeConnector connector = mock(PipeConnector.class);
+    final UnboundedBlockingPendingQueue<Event> pendingQueue =
+        mock(UnboundedBlockingPendingQueue.class);
+    final Event event = mock(Event.class);
+    final NullPointerException rootCause = new NullPointerException();
+
+    when(pendingQueue.waitedPoll()).thenReturn(event);
+    doThrow(rootCause).when(connector).transfer(any(Event.class));
+
+    final PipeSinkSubtask subtask =
+        new PipeSinkSubtask(
+            "PipeSinkSubtaskTest",
+            System.currentTimeMillis(),
+            "data_test",
+            0,
+            pendingQueue,
+            connector);
+
+    try {
+      subtask.executeOnce();
+      Assert.fail();
+    } catch (final PipeException e) {
+      Assert.assertTrue(e.getMessage().contains("root cause: 
java.lang.NullPointerException"));
+      Assert.assertFalse(e.getMessage().contains("root cause: null"));
+      Assert.assertSame(rootCause, e.getCause());
+    } finally {
+      subtask.close();
+    }
+  }
+
+  @Test
+  public void testOnFailurePreservesOriginalCause() {
+    final PipeConnector connector = mock(PipeConnector.class);
+    final UnboundedBlockingPendingQueue<Event> pendingQueue =
+        mock(UnboundedBlockingPendingQueue.class);
+    final EnrichedEvent event = mock(EnrichedEvent.class);
+    final NullPointerException rootCause = new NullPointerException();
+    final PipeException failure = new PipeException("transfer failed", 
rootCause);
+    final CapturingPipeSinkSubtask subtask = new 
CapturingPipeSinkSubtask(pendingQueue, connector);
+
+    subtask.prepareFailure(event);
+    try {
+      subtask.onFailure(failure);
+
+      final PipeRuntimeException reportedException = 
subtask.getReportedException();
+      Assert.assertTrue(reportedException instanceof 
PipeRuntimeSinkCriticalException);
+      Assert.assertSame(failure, reportedException.getCause());
+      Assert.assertSame(rootCause, 
ErrorHandlingCommonUtils.getRootCause(reportedException));
+    } finally {
+      subtask.close();
+    }
+  }
+
   @Test
   public void testHeartbeatPreservesReceiverProbeDelayException() throws 
Exception {
     final long originalSleepIntervalInitMs =
@@ -267,6 +328,37 @@ public class PipeSinkSubtaskTest {
     }
   }
 
+  private static class CapturingPipeSinkSubtask extends PipeSinkSubtask {
+
+    private PipeRuntimeException reportedException;
+
+    private CapturingPipeSinkSubtask(
+        final UnboundedBlockingPendingQueue<Event> pendingQueue, final 
PipeConnector connector) {
+      super(
+          "PipeSinkSubtaskTest",
+          System.currentTimeMillis(),
+          "data_test",
+          0,
+          pendingQueue,
+          connector);
+    }
+
+    private void prepareFailure(final EnrichedEvent event) {
+      setLastEvent(event);
+      setLastExceptionEvent(event);
+      retryCount.set(MAX_RETRY_TIMES);
+    }
+
+    private PipeRuntimeException getReportedException() {
+      return reportedException;
+    }
+
+    @Override
+    protected void report(final EnrichedEvent event, final 
PipeRuntimeException exception) {
+      reportedException = exception;
+    }
+  }
+
   private static class BlockingHandshakeConnector
       implements PipeConnector, PipeConnectorWithEventDiscard {
 
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/pipe/PipeRuntimeCriticalException.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/pipe/PipeRuntimeCriticalException.java
index c6db38e23fa..a52cfc4a18b 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/pipe/PipeRuntimeCriticalException.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/pipe/PipeRuntimeCriticalException.java
@@ -35,6 +35,10 @@ public class PipeRuntimeCriticalException extends 
PipeRuntimeException {
     super(message);
   }
 
+  public PipeRuntimeCriticalException(final String message, final Throwable 
cause) {
+    super(message, cause);
+  }
+
   public PipeRuntimeCriticalException(final String message, final long 
timeStamp) {
     super(message, timeStamp);
   }
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/pipe/PipeRuntimeException.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/pipe/PipeRuntimeException.java
index 24fa4a1edda..9e597586d5f 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/pipe/PipeRuntimeException.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/pipe/PipeRuntimeException.java
@@ -32,6 +32,10 @@ public abstract class PipeRuntimeException extends 
PipeException {
     super(message);
   }
 
+  protected PipeRuntimeException(final String message, final Throwable cause) {
+    super(message, cause);
+  }
+
   protected PipeRuntimeException(final String message, final long timeStamp) {
     super(message, timeStamp);
   }
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/pipe/PipeRuntimeSinkCriticalException.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/pipe/PipeRuntimeSinkCriticalException.java
index ccc91f7fef1..2211458324b 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/pipe/PipeRuntimeSinkCriticalException.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/pipe/PipeRuntimeSinkCriticalException.java
@@ -35,6 +35,10 @@ public class PipeRuntimeSinkCriticalException extends 
PipeRuntimeCriticalExcepti
     super(message);
   }
 
+  public PipeRuntimeSinkCriticalException(final String message, final 
Throwable cause) {
+    super(message, cause);
+  }
+
   public PipeRuntimeSinkCriticalException(final String message, final long 
timeStamp) {
     super(message, timeStamp);
   }
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/subtask/PipeAbstractSinkSubtask.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/subtask/PipeAbstractSinkSubtask.java
index 3593c96b5cd..f3c1d72605c 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/subtask/PipeAbstractSinkSubtask.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/subtask/PipeAbstractSinkSubtask.java
@@ -178,7 +178,10 @@ public abstract class PipeAbstractSinkSubtask extends 
PipeReportableSubtask {
           LOGGER::warn,
           throwable,
           "A non PipeRuntimeSinkCriticalException occurred, will throw a 
PipeRuntimeSinkCriticalException.");
-      super.onFailure(new 
PipeRuntimeSinkCriticalException(throwable.getMessage()));
+      super.onFailure(
+          new PipeRuntimeSinkCriticalException(
+              throwable.getMessage() != null ? throwable.getMessage() : 
throwable.toString(),
+              throwable));
     }
   }
 
@@ -231,7 +234,7 @@ public abstract class PipeAbstractSinkSubtask extends 
PipeReportableSubtask {
       report(
           (EnrichedEvent) lastEvent,
           new PipeRuntimeSinkCriticalException(
-              throwable.getMessage() + ", root cause: " + 
getRootCause(throwable)));
+              throwable.getMessage() + ", root cause: " + 
getRootCause(throwable), throwable));
       LOGGER.warn(
           "{} failed to handshake with the target system after {} times, "
               + "stopping current subtask {} (creation time: {}, simple class: 
{}). "
@@ -346,7 +349,7 @@ public abstract class PipeAbstractSinkSubtask extends 
PipeReportableSubtask {
                 event instanceof EnrichedEvent
                     ? ((EnrichedEvent) event).coreReportMessage()
                     : event,
-                ErrorHandlingCommonUtils.getRootCause(e).getMessage()),
+                ErrorHandlingCommonUtils.getRootCause(e).toString()),
             e);
       } else {
         LOGGER.info(

Reply via email to