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

rohangarg 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 79f882f48c1 Fix exception cause logging in QueryResultPusher  (#14975)
79f882f48c1 is described below

commit 79f882f48c15eef77d627f1d31212d6c09cae315
Author: Zoltan Haindrich <[email protected]>
AuthorDate: Wed Sep 20 12:14:02 2023 +0200

    Fix exception cause logging in QueryResultPusher  (#14975)
---
 .../apache/druid/error/QueryExceptionCompat.java   |   2 +-
 .../org/apache/druid/server/QueryResourceTest.java |  83 +++++++++++
 .../apache/druid/server/QueryResultPusherTest.java | 161 +++++++++++++++++++++
 3 files changed, 245 insertions(+), 1 deletion(-)

diff --git 
a/processing/src/main/java/org/apache/druid/error/QueryExceptionCompat.java 
b/processing/src/main/java/org/apache/druid/error/QueryExceptionCompat.java
index 9894208e9f9..163c7b04cd0 100644
--- a/processing/src/main/java/org/apache/druid/error/QueryExceptionCompat.java
+++ b/processing/src/main/java/org/apache/druid/error/QueryExceptionCompat.java
@@ -47,7 +47,7 @@ public class QueryExceptionCompat extends 
DruidException.Failure
   {
     return bob.forPersona(DruidException.Persona.OPERATOR)
               .ofCategory(convertFailType(exception.getFailType()))
-              .build(exception.getMessage())
+              .build(exception, exception.getMessage())
               .withContext("host", exception.getHost())
               .withContext("errorClass", exception.getErrorClass())
               .withContext("legacyErrorCode", exception.getErrorCode());
diff --git 
a/server/src/test/java/org/apache/druid/server/QueryResourceTest.java 
b/server/src/test/java/org/apache/druid/server/QueryResourceTest.java
index c7c96ecd3b1..6b0a2720d9d 100644
--- a/server/src/test/java/org/apache/druid/server/QueryResourceTest.java
+++ b/server/src/test/java/org/apache/druid/server/QueryResourceTest.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.jaxrs.smile.SmileMediaTypes;
 import com.google.common.base.Suppliers;
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -87,6 +88,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -375,6 +377,87 @@ public class QueryResourceTest
     );
   }
 
+
+  @Test
+  public void testQueryThrowsRuntimeExceptionFromLifecycleExecute() throws 
IOException
+  {
+    String embeddedExceptionMessage = "Embedded Exception Message!";
+    String overrideConfigKey = "priority";
+    String overrideConfigValue = "678";
+
+    DefaultQueryConfig overrideConfig = new 
DefaultQueryConfig(ImmutableMap.of(overrideConfigKey, overrideConfigValue));
+    QuerySegmentWalker querySegmentWalker = new QuerySegmentWalker()
+    {
+      @Override
+      public <T> QueryRunner<T> getQueryRunnerForIntervals(
+          Query<T> query,
+          Iterable<Interval> intervals
+      )
+      {
+        throw new RuntimeException("something", new 
RuntimeException(embeddedExceptionMessage));
+      }
+
+      @Override
+      public <T> QueryRunner<T> getQueryRunnerForSegments(
+          Query<T> query,
+          Iterable<SegmentDescriptor> specs
+      )
+      {
+        throw new UnsupportedOperationException();
+      }
+    };
+
+    queryResource = new QueryResource(
+
+        new QueryLifecycleFactory(null, null, null, null, null, null, null, 
Suppliers.ofInstance(overrideConfig))
+        {
+          @Override
+          public QueryLifecycle factorize()
+          {
+            return new QueryLifecycle(
+                WAREHOUSE,
+                querySegmentWalker,
+                new DefaultGenericQueryMetricsFactory(),
+                new NoopServiceEmitter(),
+                testRequestLogger,
+                AuthTestUtils.TEST_AUTHORIZER_MAPPER,
+                overrideConfig,
+                new AuthConfig(),
+                System.currentTimeMillis(),
+                System.nanoTime())
+            {
+              @Override
+              public void emitLogsAndMetrics(@Nullable Throwable e, @Nullable 
String remoteAddress, long bytesWritten)
+              {
+                
Assert.assertTrue(Throwables.getStackTraceAsString(e).contains(embeddedExceptionMessage));
+              }
+            };
+          }
+        },
+        jsonMapper,
+        smileMapper,
+        queryScheduler,
+        new AuthConfig(),
+        null,
+        ResponseContextConfig.newConfig(true),
+        DRUID_NODE
+    );
+
+    expectPermissiveHappyPathAuth();
+
+    final Response response = 
expectSynchronousRequestFlow(SIMPLE_TIMESERIES_QUERY);
+    Assert.assertEquals(Status.INTERNAL_SERVER_ERROR.getStatusCode(), 
response.getStatus());
+
+    final ErrorResponse entity = (ErrorResponse) response.getEntity();
+    MatcherAssert.assertThat(
+        entity.getUnderlyingException(),
+        new DruidExceptionMatcher(
+            DruidException.Persona.OPERATOR,
+            DruidException.Category.RUNTIME_FAILURE, "legacyQueryException")
+            .expectMessageIs("something")
+    );
+  }
+
   @Test
   public void testGoodQueryWithQueryConfigDoesNotOverrideQueryContext() throws 
IOException
   {
diff --git 
a/server/src/test/java/org/apache/druid/server/QueryResultPusherTest.java 
b/server/src/test/java/org/apache/druid/server/QueryResultPusherTest.java
new file mode 100644
index 00000000000..5b123b7cc2a
--- /dev/null
+++ b/server/src/test/java/org/apache/druid/server/QueryResultPusherTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.druid.server;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Throwables;
+import org.apache.druid.jackson.DefaultObjectMapper;
+import org.apache.druid.server.QueryResource.QueryMetricCounter;
+import org.apache.druid.server.QueryResultPusher.ResultsWriter;
+import org.apache.druid.server.QueryResultPusher.Writer;
+import org.apache.druid.server.mocks.MockHttpServletRequest;
+import org.junit.Test;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response.ResponseBuilder;
+
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.junit.Assert.assertTrue;
+
+public class QueryResultPusherTest
+{
+  private static final DruidNode DRUID_NODE = new DruidNode(
+      "broker",
+      "localhost",
+      true,
+      8082,
+      null,
+      true,
+      false);
+
+  @Test
+  public void testResultPusherRetainsNestedExceptionBacktraces()
+  {
+
+    HttpServletRequest request = new MockHttpServletRequest();
+    ObjectMapper jsonMapper = new DefaultObjectMapper();
+    ResponseContextConfig responseContextConfig = 
ResponseContextConfig.newConfig(true);
+    DruidNode selfNode = DRUID_NODE;
+    QueryResource.QueryMetricCounter counter = new NoopQueryMetricCounter();
+    String queryId = "someQuery";
+    MediaType contentType = MediaType.APPLICATION_JSON_TYPE;
+    Map<String, String> extraHeaders = new HashMap<String, String>();
+    AtomicBoolean recordFailureInvoked = new AtomicBoolean();
+
+    String embeddedExceptionMessage = "Embedded Exception Message!";
+    RuntimeException embeddedException = new 
RuntimeException(embeddedExceptionMessage);
+    RuntimeException topException = new RuntimeException("Where's the party?", 
embeddedException);
+
+    ResultsWriter resultWriter = new ResultsWriter()
+    {
+
+      @Override
+      public void close()
+      {
+      }
+
+      @Override
+      public ResponseBuilder start()
+      {
+        throw topException;
+      }
+
+      @Override
+      public void recordSuccess(long numBytes)
+      {
+      }
+
+      @Override
+      public void recordFailure(Exception e)
+      {
+        
assertTrue(Throwables.getStackTraceAsString(e).contains(embeddedExceptionMessage));
+        recordFailureInvoked.set(true);
+      }
+
+      @Override
+      public Writer makeWriter(OutputStream out)
+      {
+        return null;
+      }
+
+      @Override
+      public QueryResponse<Object> getQueryResponse()
+      {
+        return null;
+      }
+    };
+    QueryResultPusher pusher = new QueryResultPusher(
+        request,
+        jsonMapper,
+        responseContextConfig,
+        selfNode,
+        counter,
+        queryId,
+        contentType,
+        extraHeaders)
+    {
+
+      @Override
+      public void writeException(Exception e, OutputStream out)
+      {
+      }
+
+      @Override
+      public ResultsWriter start()
+      {
+        return resultWriter;
+      }
+    };
+
+    pusher.push();
+
+    assertTrue("recordFailure(e) should have been invoked!", 
recordFailureInvoked.get());
+  }
+
+  static class NoopQueryMetricCounter implements QueryMetricCounter
+  {
+
+    @Override
+    public void incrementSuccess()
+    {
+    }
+
+    @Override
+    public void incrementFailed()
+    {
+    }
+
+    @Override
+    public void incrementInterrupted()
+    {
+    }
+
+    @Override
+    public void incrementTimedOut()
+    {
+    }
+
+  }
+}


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

Reply via email to