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 8062f7d5e3f Prevent pipe receiver statement exception log floods 
(#18318) (#18325)
8062f7d5e3f is described below

commit 8062f7d5e3fbebc13527dbd29b6f8e508d17d776
Author: Caideyipi <[email protected]>
AuthorDate: Wed Jul 29 10:03:27 2026 +0800

    Prevent pipe receiver statement exception log floods (#18318) (#18325)
    
    (cherry picked from commit 11aa5069a6d260394c1db0c17e6a0a2ff1221d8a)
---
 .../protocol/thrift/IoTDBDataNodeReceiver.java     | 33 +++++---
 .../plan/statement/crud/InsertRowsStatement.java   | 18 +++++
 .../protocol/thrift/IoTDBDataNodeReceiverTest.java | 88 +++++++++++++++++-----
 3 files changed, 109 insertions(+), 30 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java
index f2a05a72798..6ad15376f85 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java
@@ -117,6 +117,8 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import static 
org.apache.iotdb.commons.utils.ErrorHandlingCommonUtils.getRootCause;
+
 public class IoTDBDataNodeReceiver extends IoTDBFileReceiver {
 
   private static final Logger LOGGER = 
LoggerFactory.getLogger(IoTDBDataNodeReceiver.class);
@@ -916,7 +918,7 @@ public class IoTDBDataNodeReceiver extends 
IoTDBFileReceiver {
   }
 
   private void logStatementExceptionIfNecessary(final Statement statement, 
final Exception e) {
-    if (shouldLogStatementException(receiverId.get(), statement, e)) {
+    if (shouldLogStatementException(statement, e)) {
       PipeLogger.log(
           LOGGER::warn,
           e,
@@ -926,16 +928,25 @@ public class IoTDBDataNodeReceiver extends 
IoTDBFileReceiver {
     }
   }
 
-  static boolean shouldLogStatementException(
-      final long receiverId, final Statement statement, final Exception e) {
-    // Use the reducer cache as a gate. The actual stack trace is logged only 
when it passes.
-    return LoggerPeriodicalLogReducer.log(
-        message -> {},
-        "Receiver id = %s, statement = %s, exception = %s, message = %s",
-        receiverId,
-        Objects.isNull(statement) ? null : statement.getPipeLoggingString(),
-        e.getClass().getName(),
-        e.getMessage());
+  static boolean shouldLogStatementException(final Statement statement, final 
Exception e) {
+    final Throwable rootCause = getRootCause(e);
+    final StackTraceElement[] rootCauseStackTrace = rootCause.getStackTrace();
+    final StackTraceElement rootCauseLocation =
+        rootCauseStackTrace.length > 0 ? rootCauseStackTrace[0] : null;
+
+    // Reduce exceptions raised from the same code location regardless of 
receiver, statement
+    // content, or dynamic exception message. Different statement types and 
failure locations are
+    // still logged independently.
+    return LoggerPeriodicalLogReducer.shouldLog(
+        IoTDBDataNodeReceiver.class.getName()
+            + '|'
+            + (Objects.isNull(statement)
+                ? Statement.class.getName()
+                : statement.getClass().getName())
+            + '|'
+            + rootCause.getClass().getName()
+            + '|'
+            + rootCauseLocation);
   }
 
   private TSStatus executeStatementWithRetryOnDataTypeMismatch(final Statement 
statement) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsStatement.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsStatement.java
index 0e7b65371b5..0a4a84ad032 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsStatement.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertRowsStatement.java
@@ -180,6 +180,24 @@ public class InsertRowsStatement extends 
InsertBaseStatement {
             : 0);
   }
 
+  @Override
+  public String getPipeLoggingString() {
+    if (Objects.isNull(insertRowStatementList) || 
insertRowStatementList.isEmpty()) {
+      return "InsertRowsStatement{rowCount=0}";
+    }
+
+    final int rowCount = insertRowStatementList.size();
+    return "InsertRowsStatement{"
+        + "rowCount="
+        + rowCount
+        + ", firstRow="
+        + insertRowStatementList.get(0).getPipeLoggingString()
+        + (rowCount > 1
+            ? ", lastRow=" + insertRowStatementList.get(rowCount - 
1).getPipeLoggingString()
+            : "")
+        + '}';
+  }
+
   @Override
   public String toString() {
     return "InsertRowsStatement{" + "insertRowStatementList=" + 
insertRowStatementList + '}';
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java
index 864d0493979..a2710535f70 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java
@@ -20,6 +20,8 @@
 package org.apache.iotdb.db.pipe.receiver.protocol.thrift;
 
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement;
+import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsStatement;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.LoadTsFileStatement;
 import org.apache.iotdb.db.storageengine.load.active.ActiveLoadPathHelper;
 import org.apache.iotdb.db.storageengine.load.config.LoadTsFileConfigurator;
@@ -29,6 +31,7 @@ import org.junit.Test;
 
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Arrays;
 import java.util.Map;
 
 public class IoTDBDataNodeReceiverTest {
@@ -89,25 +92,72 @@ public class IoTDBDataNodeReceiverTest {
   }
 
   @Test
-  public void testRepeatedStatementExceptionLogIsReduced() throws Exception {
-    final Path tsFile = Files.createTempFile("pipe-load-log-reducer", 
".tsfile");
-    try {
-      final LoadTsFileStatement statement =
-          IoTDBDataNodeReceiver.buildLoadTsFileStatementForSync(
-              "root.test.sg_0", tsFile.toString(), true, true);
-      final long receiverId = System.nanoTime();
-      final Exception exception = new RuntimeException("repeated receiver 
exception " + receiverId);
-
-      Assert.assertTrue(
-          IoTDBDataNodeReceiver.shouldLogStatementException(receiverId, 
statement, exception));
-      Assert.assertFalse(
-          IoTDBDataNodeReceiver.shouldLogStatementException(receiverId, 
statement, exception));
-      Assert.assertTrue(
-          IoTDBDataNodeReceiver.shouldLogStatementException(
-              receiverId, statement, new RuntimeException("another receiver 
exception")));
-    } finally {
-      Files.deleteIfExists(tsFile);
-    }
+  public void testStatementExceptionLogIsReducedByFailureLocation() {
+    final InsertRowStatement firstStatement = new InsertRowStatement();
+    firstStatement.setTime(1);
+    firstStatement.setValues(new Object[] {"first statement value"});
+    final InsertRowStatement secondStatement = new InsertRowStatement();
+    secondStatement.setTime(2);
+    secondStatement.setValues(new Object[] {"second statement value"});
+
+    final String testId = Long.toString(System.nanoTime());
+    final String sameFailureLocation = "sameFailureLocation" + testId;
+    Assert.assertTrue(
+        IoTDBDataNodeReceiver.shouldLogStatementException(
+            firstStatement, newStatementException("first message", 
sameFailureLocation)));
+    Assert.assertFalse(
+        IoTDBDataNodeReceiver.shouldLogStatementException(
+            secondStatement, newStatementException("second message", 
sameFailureLocation)));
+    Assert.assertTrue(
+        IoTDBDataNodeReceiver.shouldLogStatementException(
+            secondStatement,
+            newStatementException("second message", "differentFailureLocation" 
+ testId)));
+    Assert.assertTrue(
+        IoTDBDataNodeReceiver.shouldLogStatementException(
+            new InsertRowsStatement(),
+            newStatementException("second message", sameFailureLocation)));
+  }
+
+  @Test
+  public void testInsertRowsPipeLoggingStringIsCompact() {
+    final InsertRowStatement firstStatement = new InsertRowStatement();
+    firstStatement.setTime(1);
+    firstStatement.setValues(new Object[] {"first secret value"});
+    final InsertRowStatement middleStatement = new InsertRowStatement();
+    middleStatement.setTime(2);
+    middleStatement.setValues(new Object[] {"middle secret value"});
+    final InsertRowStatement lastStatement = new InsertRowStatement();
+    lastStatement.setTime(3);
+    lastStatement.setValues(new Object[] {"last secret value"});
+
+    final InsertRowsStatement statement = new InsertRowsStatement();
+    statement.setInsertRowStatementList(
+        Arrays.asList(firstStatement, middleStatement, lastStatement));
+
+    final String pipeLoggingString = statement.getPipeLoggingString();
+    Assert.assertTrue(pipeLoggingString.contains("rowCount=3"));
+    Assert.assertTrue(pipeLoggingString.contains("firstRow="));
+    Assert.assertTrue(pipeLoggingString.contains("time=1"));
+    Assert.assertTrue(pipeLoggingString.contains("lastRow="));
+    Assert.assertTrue(pipeLoggingString.contains("time=3"));
+    Assert.assertFalse(pipeLoggingString.contains("time=2"));
+    Assert.assertFalse(pipeLoggingString.contains("first secret value"));
+    Assert.assertFalse(pipeLoggingString.contains("middle secret value"));
+    Assert.assertFalse(pipeLoggingString.contains("last secret value"));
+  }
+
+  private static Exception newStatementException(
+      final String message, final String failureLocation) {
+    final NullPointerException rootCause = new NullPointerException(message);
+    rootCause.setStackTrace(
+        new StackTraceElement[] {
+          new StackTraceElement(
+              IoTDBDataNodeReceiverTest.class.getName(),
+              failureLocation,
+              "IoTDBDataNodeReceiverTest.java",
+              1)
+        });
+    return new RuntimeException("wrapper " + message, rootCause);
   }
 
   @Test

Reply via email to