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

maytasm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 5417aa2055 Fix: ParseException swallow cause Exception (#12810)
5417aa2055 is described below

commit 5417aa2055b3abb4b4515f9de81397a19e5ebc73
Author: Maytas Monsereenusorn <[email protected]>
AuthorDate: Fri Jul 22 13:46:28 2022 -0700

    Fix: ParseException swallow cause Exception (#12810)
    
    * add impl
    
    * add impl
    
    * fix checkstyle
---
 .../java/util/common/parsers/ParseException.java   | 20 ++++---
 .../FilteringCloseableInputRowIteratorTest.java    | 69 +++++++++++++++++++++-
 .../segment/incremental/ParseExceptionHandler.java | 13 +++-
 3 files changed, 87 insertions(+), 15 deletions(-)

diff --git 
a/core/src/main/java/org/apache/druid/java/util/common/parsers/ParseException.java
 
b/core/src/main/java/org/apache/druid/java/util/common/parsers/ParseException.java
index fc9877de5b..e72c89518b 100644
--- 
a/core/src/main/java/org/apache/druid/java/util/common/parsers/ParseException.java
+++ 
b/core/src/main/java/org/apache/druid/java/util/common/parsers/ParseException.java
@@ -57,23 +57,25 @@ public class ParseException extends RuntimeException
 
   public ParseException(@Nullable String input, String formatText, Object... 
arguments)
   {
-    super(StringUtils.nonStrictFormat(formatText, arguments));
-    this.input = input;
-    this.fromPartiallyValidRow = false;
-    this.timeOfExceptionMillis = System.currentTimeMillis();
+    this(input, false, formatText, arguments);
   }
 
   public ParseException(@Nullable String input, boolean fromPartiallyValidRow, 
String formatText, Object... arguments)
   {
-    super(StringUtils.nonStrictFormat(formatText, arguments));
-    this.input = input;
-    this.fromPartiallyValidRow = fromPartiallyValidRow;
-    this.timeOfExceptionMillis = System.currentTimeMillis();
+    this(input, fromPartiallyValidRow, null, formatText, arguments);
   }
 
   public ParseException(@Nullable String input, Throwable cause, String 
formatText, Object... arguments)
   {
-    this(input, false, StringUtils.nonStrictFormat(formatText, arguments), 
cause);
+    this(input, false, cause, formatText, arguments);
+  }
+
+  public ParseException(@Nullable String input, boolean fromPartiallyValidRow, 
Throwable cause, String formatText, Object... arguments)
+  {
+    super(StringUtils.nonStrictFormat(formatText, arguments), cause);
+    this.input = input;
+    this.fromPartiallyValidRow = fromPartiallyValidRow;
+    this.timeOfExceptionMillis = System.currentTimeMillis();
   }
 
   public boolean isFromPartiallyValidRow()
diff --git 
a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/FilteringCloseableInputRowIteratorTest.java
 
b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/FilteringCloseableInputRowIteratorTest.java
index 844021520a..a0b1c101d4 100644
--- 
a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/FilteringCloseableInputRowIteratorTest.java
+++ 
b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/FilteringCloseableInputRowIteratorTest.java
@@ -35,6 +35,8 @@ import org.joda.time.DateTime;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -62,12 +64,12 @@ public class FilteringCloseableInputRowIteratorTest
   public void setup()
   {
     rowIngestionMeters = new SimpleRowIngestionMeters();
-    parseExceptionHandler = new ParseExceptionHandler(
+    parseExceptionHandler = Mockito.spy(new ParseExceptionHandler(
         rowIngestionMeters,
-        false,
+        true,
         Integer.MAX_VALUE,
         1024 // do not use Integer.MAX_VALUE since it will create an object 
array of this length
-    );
+    ));
   }
 
   @Test
@@ -287,6 +289,67 @@ public class FilteringCloseableInputRowIteratorTest
     Assert.assertTrue(closed.isTrue());
   }
 
+  @Test
+  public void testParseExceptionSaveExceptionCause()
+  {
+
+    // This iterator throws ParseException every other call to hasNext().
+    final CloseableIterator<InputRow> parseExceptionThrowingIterator = new 
CloseableIterator<InputRow>()
+    {
+      final int numRowsToIterate = ROWS.size() * 2;
+      int currentIndex = 0;
+      int nextIndex = 0;
+
+      @Override
+      public boolean hasNext()
+      {
+        currentIndex = nextIndex++;
+        if (currentIndex % 2 == 0) {
+          return currentIndex < numRowsToIterate;
+        } else {
+          try {
+            throw new IllegalArgumentException("this is the root cause of the 
exception!");
+          }
+          catch (Exception e) {
+            throw new ParseException(null, e, "Parse exception at [%d]", 
currentIndex);
+          }
+        }
+      }
+
+      @Override
+      public InputRow next()
+      {
+        return ROWS.get(currentIndex / 2);
+      }
+
+      @Override
+      public void close()
+      {
+      }
+    };
+
+    final FilteringCloseableInputRowIterator rowIterator = new 
FilteringCloseableInputRowIterator(
+        parseExceptionThrowingIterator,
+        row -> true,
+        rowIngestionMeters,
+        parseExceptionHandler
+    );
+
+    final List<InputRow> filteredRows = new ArrayList<>();
+    rowIterator.forEachRemaining(filteredRows::add);
+    ArgumentCaptor<Exception> exceptionArgumentCaptor = 
ArgumentCaptor.forClass(Exception.class);
+    Mockito.verify(parseExceptionHandler, 
Mockito.times(6)).logParseExceptionHelper(exceptionArgumentCaptor.capture());
+    Exception parseException = exceptionArgumentCaptor.getValue();
+    Assert.assertTrue(parseException.getMessage().contains("Parse exception 
at"));
+    Assert.assertNotNull(parseException.getCause());
+    Assert.assertTrue(parseException.getCause().getMessage().contains("this is 
the root cause of the exception!"));
+    Assert.assertEquals(IllegalArgumentException.class, 
parseException.getCause().getClass());
+
+    Assert.assertEquals(ROWS, filteredRows);
+    Assert.assertEquals(ROWS.size(), rowIngestionMeters.getUnparseable());
+  }
+
+
   private static InputRow newRow(DateTime timestamp, Object dim1Val, Object 
dim2Val)
   {
     return new MapBasedInputRow(timestamp, DIMENSIONS, ImmutableMap.of("dim1", 
dim1Val, "dim2", dim2Val));
diff --git 
a/processing/src/main/java/org/apache/druid/segment/incremental/ParseExceptionHandler.java
 
b/processing/src/main/java/org/apache/druid/segment/incremental/ParseExceptionHandler.java
index 18ecdd06fb..a67da71727 100644
--- 
a/processing/src/main/java/org/apache/druid/segment/incremental/ParseExceptionHandler.java
+++ 
b/processing/src/main/java/org/apache/druid/segment/incremental/ParseExceptionHandler.java
@@ -19,6 +19,7 @@
 
 package org.apache.druid.segment.incremental;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import org.apache.druid.java.util.common.RE;
@@ -77,9 +78,7 @@ public class ParseExceptionHandler
       rowIngestionMeters.incrementUnparseable();
     }
 
-    if (logParseExceptions) {
-      LOG.error(e, "Encountered parse exception");
-    }
+    logParseExceptionHelper(e);
 
     if (savedParseExceptionReports != null) {
       ParseExceptionReport parseExceptionReport = new ParseExceptionReport(
@@ -103,4 +102,12 @@ public class ParseExceptionHandler
   {
     return savedParseExceptionReports;
   }
+
+  @VisibleForTesting
+  public void logParseExceptionHelper(Exception e)
+  {
+    if (logParseExceptions) {
+      LOG.error(e, "Encountered parse exception");
+    }
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to