This is an automated email from the ASF dual-hosted git repository.
gian 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 c3262756f1d feat: MSQ exception handling improvements. (#19234)
c3262756f1d is described below
commit c3262756f1d5eb8fd992e93b54a27c3181ed9bfa
Author: Gian Merlino <[email protected]>
AuthorDate: Wed Apr 1 10:37:55 2026 -0700
feat: MSQ exception handling improvements. (#19234)
This patch makes various changes to MSQ exception handling, with the
goal of better compatibility with DruidExceptions:
1) Add DruidExceptionFault, which allows including DruidExceptions in
MSQErrorReports without losing any information.
2) Make MSQFault#toDruidException abstract so all fault implementations
must reference a specific category and persona. Changed the category
and persona of various faults, in situations where the inherited
default was not ideal.
3) QueryRuntimeFault no longer generated, instead we're relying fully
on DruidException for exceptions that originate in the query runtime.
4) Retain original DruidException, rather than wrapping and re-throwing,
in ControllerImpl, SegmentGeneratorFrameProcessor, and
RowBasedFrameWriter.
5) Update Either to throw non-DEVELOPER DruidExceptions as-is, rather
than wrapping them.
---
docs/multi-stage-query/reference.md | 6 +-
.../dart/worker/DartDataServerQueryHandler.java | 2 +-
.../org/apache/druid/msq/exec/ControllerImpl.java | 4 +-
.../java/org/apache/druid/msq/exec/MSQTasks.java | 20 +-
.../apache/druid/msq/guice/MSQIndexingModule.java | 2 +
.../error/BroadcastTablesTooLargeFault.java | 10 +
.../druid/msq/indexing/error/CanceledFault.java | 4 +-
.../msq/indexing/error/CancellationReason.java | 33 +-
.../error/CannotParseExternalDataFault.java | 10 +
.../msq/indexing/error/DruidExceptionFault.java | 152 ++++++++
.../error/DurableStorageConfigurationFault.java | 10 +
.../error/InsertCannotAllocateSegmentFault.java | 10 +
.../indexing/error/InsertCannotBeEmptyFault.java | 10 +
.../indexing/error/InsertLockPreemptedFault.java | 10 +
.../msq/indexing/error/InsertTimeNullFault.java | 10 +
.../indexing/error/InsertTimeOutOfBoundsFault.java | 10 +
.../msq/indexing/error/InvalidFieldFault.java | 13 +
.../msq/indexing/error/InvalidNullByteFault.java | 10 +
.../druid/msq/indexing/error/MSQErrorReport.java | 37 +-
.../apache/druid/msq/indexing/error/MSQFault.java | 11 +-
.../msq/indexing/error/NotEnoughMemoryFault.java | 10 +
.../error/NotEnoughTemporaryStorageFault.java | 10 +
.../msq/indexing/error/QueryNotSupportedFault.java | 2 +-
.../msq/indexing/error/QueryRuntimeFault.java | 15 +-
.../druid/msq/indexing/error/RowTooLargeFault.java | 10 +
.../msq/indexing/error/TaskStartTimeoutFault.java | 10 +
.../msq/indexing/error/TooManyAttemptsForJob.java | 10 +
.../indexing/error/TooManyAttemptsForWorker.java | 10 +
.../msq/indexing/error/TooManyBucketsFault.java | 10 +
.../error/TooManyClusteredByColumnsFault.java | 10 +
.../msq/indexing/error/TooManyColumnsFault.java | 10 +
.../msq/indexing/error/TooManyInputFilesFault.java | 10 +
.../msq/indexing/error/TooManyPartitionsFault.java | 10 +
.../indexing/error/TooManyRowsInAWindowFault.java | 10 +
.../error/TooManyRowsWithSameKeyFault.java | 10 +
.../error/TooManySegmentsInTimeChunkFault.java | 10 +
.../msq/indexing/error/TooManyWarningsFault.java | 10 +
.../msq/indexing/error/TooManyWorkersFault.java | 10 +
.../druid/msq/indexing/error/UnknownFault.java | 10 +
.../msq/indexing/error/WorkerFailedFault.java | 10 +
.../msq/indexing/error/WorkerRpcFailedFault.java | 10 +
.../processor/SegmentGeneratorFrameProcessor.java | 2 +
.../druid/msq/input/RegularLoadableSegment.java | 2 +-
.../results/ExportResultsFrameProcessor.java | 4 +-
.../msq/sql/resources/SqlStatementResource.java | 2 +-
.../dart/controller/http/DartSqlResourceTest.java | 4 +-
.../org/apache/druid/msq/exec/MSQFaultsTest.java | 27 ++
.../org/apache/druid/msq/exec/MSQSelectTest.java | 4 +-
.../msq/indexing/error/MSQErrorReportTest.java | 6 +-
.../msq/indexing/error/MSQFaultSerdeTest.java | 14 +
.../error/MSQFaultToDruidExceptionTest.java | 411 +++++++++++++++++++++
.../druid/frame/write/RowBasedFrameWriter.java | 5 +-
.../org/apache/druid/java/util/common/Either.java | 25 +-
.../java/org/apache/druid/common/EitherTest.java | 29 ++
.../funcs_and_sql_func_json_keys.03.dart.iq | 18 +-
.../funcs_and_sql_func_json_paths.02.dart.iq | 6 +-
56 files changed, 1066 insertions(+), 74 deletions(-)
diff --git a/docs/multi-stage-query/reference.md
b/docs/multi-stage-query/reference.md
index 29b044b46b8..b2311382052 100644
--- a/docs/multi-stage-query/reference.md
+++ b/docs/multi-stage-query/reference.md
@@ -582,6 +582,7 @@ The following table lists query limits:
| Number of workers for any one stage. | Hard limit is 1,000. Memory-dependent
soft limit may be lower. | `TooManyWorkers`|
| Maximum memory occupied by broadcasted tables. | 30% of each [processor
memory bundle](concepts.md#memory-usage). | `BroadcastTablesTooLarge` |
| Maximum memory occupied by buffered data during sort-merge join. Only
relevant when `sqlJoinAlgorithm` is `sortMerge`. | 10 MB |
`TooManyRowsWithSameKey` |
+| Number of rows materialized in a single window partition. Configurable with
`maxRowsMaterializedInWindow`. | 100,000 | `TooManyRowsInAWindow` |
| Maximum relaunch attempts per worker. Initial run is not a relaunch. The
worker will be spawned 1 + `workerRelaunchLimit` times before the job fails. |
2 | `TooManyAttemptsForWorker` |
| Maximum relaunch attempts for a job across all workers. | 100 |
`TooManyAttemptsForJob` |
<a name="errors"></a>
@@ -597,6 +598,8 @@ The following table describes error codes you may encounter
in the `multiStageQu
| `CannotParseExternalData`| A worker task could not parse data from an
external datasource. | `errorMessage`: More details on why parsing failed. |
| `ColumnNameRestricted`| The query uses a restricted column name. |
`columnName`: The restricted column name. |
| `ColumnTypeNotSupported` | The column type is not supported. This can be
because:<br /> <br /><ul><li>Support for writing or reading from a particular
column type is not supported.</li><li>The query attempted to use a column type
that is not supported by the frame format. This occurs with ARRAY types, which
are not yet implemented for frames.</li></ul> | `columnName`: The column name
with an unsupported type.<br /> <br />`columnType`: The unknown column type. |
+| `DruidException` | An error occurred with structured information such as
code, persona, and category. It does not fall into one of the other categories.
| `druidErrorCode`: More specific error code.<br /><br />`persona`: Target
persona.<br /><br />`category`: Error category.<br /><br />`context`:
Additional context. |
+| `DurableStorageConfiguration` | Durable storage mode was enabled but is not
configured correctly. Check the documentation on how to enable durable storage
mode. | `message`: Details on the configuration error. |
|`InsertCannotAllocateSegment`| The controller task could not allocate a new
segment ID due to conflict with existing segments or pending segments. Common
reasons for such conflicts:<br /> <br /><ul><li>Attempting to mix different
granularities in the same intervals of the same datasource.</li><li>Prior
ingestions that used non-extendable shard specs.</li></ul> <br /> <br /> Use
REPLACE to overwrite the existing data or if the error contains the
`allocatedInterval` then alternatively rer [...]
|`InsertCannotBeEmpty`| An INSERT or REPLACE query did not generate any output
rows when `failOnEmptyInsert` query context is set to true. `failOnEmptyInsert`
defaults to false, so an INSERT query generating no output rows will be no-op,
and a REPLACE query generating no output rows will delete all data that matches
the OVERWRITE clause. | `dataSource` |
| `InsertLockPreempted` | An INSERT or REPLACE query was canceled by a
higher-priority ingestion job, such as a real-time ingestion task. | |
@@ -605,7 +608,6 @@ The following table describes error codes you may encounter
in the `multiStageQu
| `InvalidField`| An error was encountered while writing a field. | `error`:
Encountered error. <br /><br /> `source`: Source for the error. <br /><br />
`rowNumber`: Row number (1-indexed) for the error. <br /><br /> `column`:
Column for the error. |
| `InvalidNullByte`| A string column included a null byte. Null bytes in
strings are not permitted. |`source`: The source that included the null byte
<br /><br /> `rowNumber`: The row number (1-indexed) that included the null
byte <br /><br /> `column`: The column that included the null byte <br /><br />
`value`: Actual string containing the null byte <br /><br /> `position`:
Position (1-indexed) of occurrence of null byte|
| `QueryNotSupported`| QueryKit could not translate the provided native query
to a multi-stage query.<br /> <br />This can happen if the query uses features
that aren't supported, like GROUPING SETS. | |
-| `QueryRuntimeError` | MSQ uses the native query engine to run the leaf
stages. This error tells MSQ that error is in native query runtime.<br /> <br
/> Since this is a generic error, the user needs to look at logs for the error
message and stack trace to figure out the next course of action. If the user is
stuck, consider raising a `github` issue for assistance. | `baseErrorMessage`
error message from the native query runtime. |
| `RowTooLarge`| The query tried to process a row that was too large to write
to a single frame. See the [Limits](#limits) table for specific limits on frame
size. Note that the effective maximum row size is smaller than the maximum
frame size due to alignment considerations during frame writing. |
`maxFrameSize`: The limit on the frame size. |
| `TaskStartTimeout` | Unable to launch `pendingTasks` worker out of total
`totalTasks` workers tasks within `timeout` seconds of the last successful
worker launch.<br /><br />There may be insufficient available slots to start
all the worker tasks simultaneously. Try splitting up your query into smaller
chunks using a smaller value of [`maxNumTasks`](#context-parameters). Another
option is to increase capacity. | `pendingTasks`: Number of tasks not yet
started.<br /><br />`totalTasks`: T [...]
| `TooManyAttemptsForJob` | Total relaunch attempt count across all workers
exceeded max relaunch attempt limit. See the [Limits](#limits) table for the
specific limit. | `maxRelaunchCount`: Max number of relaunches across all the
workers defined in the [Limits](#limits) section. <br /><br />
`currentRelaunchCount`: current relaunch counter for the job across all
workers. <br /><br /> `taskId`: Latest task id which failed <br /> <br />
`rootErrorMessage`: Error message of the latest fail [...]
@@ -615,6 +617,8 @@ The following table describes error codes you may encounter
in the `multiStageQu
| `TooManyPartitions`| Exceeded the maximum number of partitions for a stage
(25,000 partitions).<br /><br />This can occur with INSERT or REPLACE
statements that generate large numbers of segments, since each segment is
associated with a partition. If you encounter this limit, consider breaking up
your INSERT or REPLACE statement into smaller statements that process less data
per statement. | `maxPartitions`: The limit on partitions which was exceeded |
| `TooManyClusteredByColumns` | Exceeded the maximum number of clustering
columns for a stage (1,500 columns).<br /><br />This can occur with `CLUSTERED
BY`, `ORDER BY`, or `GROUP BY` with a large number of columns. | `numColumns`:
The number of columns requested.<br /><br />`maxColumns`: The limit on columns
which was exceeded.`stage`: The stage number exceeding the limit<br /><br /> |
| `TooManyRowsWithSameKey` | The number of rows for a given key exceeded the
maximum number of buffered bytes on both sides of a join. See the
[Limits](#limits) table for the specific limit. Only occurs when join is
executed via the sort-merge join algorithm. | `key`: The key that had a large
number of rows.<br /><br />`numBytes`: Number of bytes buffered, which may
include other keys.<br /><br />`maxBytes`: Maximum number of bytes buffered. |
+| `TooManyRowsInAWindow` | Too many rows materialized in a single window
partition. Try using a window with a higher cardinality `PARTITION BY` column,
changing the query shape, or increasing the limit with the
`maxRowsMaterializedInWindow` query context parameter. | `numRows`: Number of
rows encountered.<br /><br />`maxRows`: Maximum number of rows allowed. |
+| `TooManySegmentsInTimeChunk` | Too many segments requested to be generated
in a single time chunk. Try breaking up the query or increase the limit using
the `maxNumSegments` query context parameter. | `timeChunk`: The time chunk
that exceeded the limit.<br /><br />`numSegments`: Number of segments
requested.<br /><br />`maxNumSegments`: Maximum number of segments allowed.<br
/><br />`segmentGranularity`: The segment granularity. |
| `TooManyColumns` | Exceeded the maximum number of columns for a stage (2,000
columns). | `numColumns`: The number of columns requested.<br /><br
/>`maxColumns`: The limit on columns which was exceeded. |
| `TooManyWarnings` | Exceeded the maximum allowed number of warnings of a
particular type. | `rootErrorCode`: The error code corresponding to the
exception that exceeded the required limit. <br /><br />`maxWarnings`: Maximum
number of warnings that are allowed for the corresponding `rootErrorCode`. |
| `TooManyWorkers`| Exceeded the maximum number of simultaneously-running
workers. See the [Limits](#limits) table for more details. | `workers`: The
number of simultaneously running workers that exceeded a hard or soft limit.
This may be larger than the number of workers in any one stage if multiple
stages are running simultaneously. <br /><br />`maxWorkers`: The hard or soft
limit on workers that was exceeded. If this is lower than the hard limit (1,000
workers), then you can increase [...]
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/dart/worker/DartDataServerQueryHandler.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/dart/worker/DartDataServerQueryHandler.java
index e81f6ee92dc..28601da4a76 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/dart/worker/DartDataServerQueryHandler.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/dart/worker/DartDataServerQueryHandler.java
@@ -130,7 +130,7 @@ public class DartDataServerQueryHandler implements
DataServerQueryHandler
if (!missingSegments.isEmpty()) {
throw DruidException
- .forPersona(DruidException.Persona.USER)
+ .forPersona(DruidException.Persona.OPERATOR)
.ofCategory(DruidException.Category.RUNTIME_FAILURE)
.build(
"Segment[%s]%s not found on server[%s]. Please retry your
query.",
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/exec/ControllerImpl.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/exec/ControllerImpl.java
index 958589598e3..082ab512d42 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/exec/ControllerImpl.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/exec/ControllerImpl.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.exec;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
@@ -698,7 +699,7 @@ public class ControllerImpl implements Controller
}
}
catch (IOException e) {
- throw DruidException.forPersona(DruidException.Persona.USER)
+ throw DruidException.forPersona(DruidException.Persona.OPERATOR)
.ofCategory(DruidException.Category.RUNTIME_FAILURE)
.build(e, "Exception occurred while connecting to export
destination.");
}
@@ -2253,6 +2254,7 @@ public class ControllerImpl implements Controller
exec.cancel(RESULT_READER_CANCELLATION_ID);
}
catch (Exception e) {
+ Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
}
finally {
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/exec/MSQTasks.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/exec/MSQTasks.java
index 75dc1271e3a..b47124aef6b 100644
--- a/multi-stage-query/src/main/java/org/apache/druid/msq/exec/MSQTasks.java
+++ b/multi-stage-query/src/main/java/org/apache/druid/msq/exec/MSQTasks.java
@@ -21,17 +21,18 @@ package org.apache.druid.msq.exec;
import com.google.inject.Injector;
import com.google.inject.Key;
+import org.apache.druid.error.DruidException;
import org.apache.druid.java.util.common.ISE;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.msq.guice.MultiStageQuery;
import org.apache.druid.msq.indexing.error.CanceledFault;
+import org.apache.druid.msq.indexing.error.DruidExceptionFault;
import org.apache.druid.msq.indexing.error.DurableStorageConfigurationFault;
import org.apache.druid.msq.indexing.error.InsertTimeNullFault;
import org.apache.druid.msq.indexing.error.MSQErrorReport;
import org.apache.druid.msq.indexing.error.MSQException;
import org.apache.druid.msq.indexing.error.MSQFault;
import org.apache.druid.msq.indexing.error.MSQFaultUtils;
-import org.apache.druid.msq.indexing.error.QueryRuntimeFault;
import org.apache.druid.msq.indexing.error.TooManyAttemptsForJob;
import org.apache.druid.msq.indexing.error.TooManyAttemptsForWorker;
import org.apache.druid.msq.indexing.error.UnknownFault;
@@ -221,8 +222,7 @@ public class MSQTasks
logMessage.append(":
").append(MSQFaultUtils.generateMessageWithErrorCode(errorReport.getFault()));
if (errorReport.getExceptionStackTrace() != null) {
- if (errorReport.getFault() instanceof UnknownFault ||
errorReport.getFault() instanceof QueryRuntimeFault) {
- // Log full stack trace for UnknownFault and QueryRuntimeFault
+ if (logFullStackTrace(errorReport.getFault())) {
logMessage.append('\n').append(errorReport.getExceptionStackTrace());
} else {
// Log first line only (error class, message) for known faults, to
avoid polluting logs.
@@ -237,4 +237,18 @@ public class MSQTasks
return logMessage.toString();
}
+
+ /**
+ * Log full stack trace for UnknownFault and non-USER DruidExceptions.
+ */
+ static boolean logFullStackTrace(final MSQFault fault)
+ {
+ if (fault instanceof UnknownFault) {
+ return true;
+ } else if (fault instanceof DruidExceptionFault) {
+ return !DruidException.Persona.USER.name().equals(((DruidExceptionFault)
fault).getPersona());
+ } else {
+ return false;
+ }
+ }
}
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/guice/MSQIndexingModule.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/guice/MSQIndexingModule.java
index 6bcbc04981f..6b100e078b4 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/guice/MSQIndexingModule.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/guice/MSQIndexingModule.java
@@ -47,6 +47,7 @@ import org.apache.druid.msq.indexing.error.CanceledFault;
import org.apache.druid.msq.indexing.error.CannotParseExternalDataFault;
import org.apache.druid.msq.indexing.error.ColumnNameRestrictedFault;
import org.apache.druid.msq.indexing.error.ColumnTypeNotSupportedFault;
+import org.apache.druid.msq.indexing.error.DruidExceptionFault;
import org.apache.druid.msq.indexing.error.DurableStorageConfigurationFault;
import org.apache.druid.msq.indexing.error.InsertCannotAllocateSegmentFault;
import org.apache.druid.msq.indexing.error.InsertCannotBeEmptyFault;
@@ -130,6 +131,7 @@ public class MSQIndexingModule implements DruidModule
CannotParseExternalDataFault.class,
ColumnTypeNotSupportedFault.class,
ColumnNameRestrictedFault.class,
+ DruidExceptionFault.class,
DurableStorageConfigurationFault.class,
InsertCannotAllocateSegmentFault.class,
InsertCannotBeEmptyFault.class,
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/BroadcastTablesTooLargeFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/BroadcastTablesTooLargeFault.java
index 31337cbcfe6..fe561c6063a 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/BroadcastTablesTooLargeFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/BroadcastTablesTooLargeFault.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.query.JoinAlgorithm;
import org.apache.druid.sql.calcite.planner.PlannerContext;
@@ -65,6 +66,15 @@ public class BroadcastTablesTooLargeFault extends
BaseMSQFault
return configuredJoinAlgorithm;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/CanceledFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/CanceledFault.java
index 329a511b0a8..29ec730fa41 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/CanceledFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/CanceledFault.java
@@ -36,7 +36,7 @@ public class CanceledFault extends BaseMSQFault
@JsonCreator
public CanceledFault(@JsonProperty("reason") @Nullable CancellationReason
reason)
{
- super(CODE, "Query canceled due to [%s].", reason == null ?
CancellationReason.UNKNOWN : reason);
+ super(CODE, (reason == null ? CancellationReason.UNKNOWN :
reason).getMessage());
this.reason = reason == null ? CancellationReason.UNKNOWN : reason;
}
@@ -70,7 +70,7 @@ public class CanceledFault extends BaseMSQFault
public DruidException toDruidException()
{
return DruidException.forPersona(DruidException.Persona.USER)
- .ofCategory(DruidException.Category.CANCELED)
+ .ofCategory(reason.getCategory())
.withErrorCode(getErrorCode())
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
}
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/CancellationReason.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/CancellationReason.java
index 3e178fa54e6..4d8c5c7968e 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/CancellationReason.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/CancellationReason.java
@@ -20,6 +20,7 @@
package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
+import org.apache.druid.error.DruidException;
/**
* Enum denoting the reason for query cancellation.
@@ -27,27 +28,39 @@ import com.fasterxml.jackson.annotation.JsonCreator;
public enum CancellationReason
{
/**
- * Query was cancaled due to the task shutting down.
+ * Query was canceled due to the task shutting down.
*/
- TASK_SHUTDOWN("Task shutdown"),
+ TASK_SHUTDOWN("Query canceled due to task shutdown",
DruidException.Category.CANCELED),
/**
- * Query was cancaled due to a request from the user.
+ * Query was canceled due to a request from the user.
*/
- USER_REQUEST("User request"),
+ USER_REQUEST("Query canceled", DruidException.Category.CANCELED),
/**
* Query was canceled due to exceeding the configured query timeout.
*/
- QUERY_TIMEOUT("Query timeout"),
+ QUERY_TIMEOUT("Query timed out", DruidException.Category.TIMEOUT),
/**
* Query was canceled due to an unknown reason.
*/
- UNKNOWN("Unknown");
+ UNKNOWN("Query interrupted", DruidException.Category.CANCELED);
- private final String reason;
+ private final String message;
+ private final DruidException.Category category;
- CancellationReason(String reason)
+ CancellationReason(final String message, final DruidException.Category
category)
{
- this.reason = reason;
+ this.message = message;
+ this.category = category;
+ }
+
+ public String getMessage()
+ {
+ return message;
+ }
+
+ public DruidException.Category getCategory()
+ {
+ return category;
}
@JsonCreator
@@ -66,6 +79,6 @@ public enum CancellationReason
@Override
public String toString()
{
- return reason;
+ return message;
}
}
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/CannotParseExternalDataFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/CannotParseExternalDataFault.java
index 7668b68ea03..769e592b49c 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/CannotParseExternalDataFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/CannotParseExternalDataFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.google.common.base.Preconditions;
+import org.apache.druid.error.DruidException;
@JsonTypeName(CannotParseExternalDataFault.CODE)
public class CannotParseExternalDataFault extends BaseMSQFault
@@ -32,4 +33,13 @@ public class CannotParseExternalDataFault extends
BaseMSQFault
{
super(CODE, Preconditions.checkNotNull(message, "errorMessage"));
}
+
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.INVALID_INPUT)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
}
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/DruidExceptionFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/DruidExceptionFault.java
new file mode 100644
index 00000000000..32dded914ee
--- /dev/null
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/DruidExceptionFault.java
@@ -0,0 +1,152 @@
+/*
+ * 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.msq.indexing.error;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Fault that wraps a {@link DruidException}, preserving its fields for
serialization across workers and controllers.
+ *
+ * Fields are stored as Strings rather than enums so that a newer worker can
send a fault to an older controller
+ * without causing Jackson deserialization failures from unknown enum values.
+ */
+@JsonTypeName(DruidExceptionFault.CODE)
+public class DruidExceptionFault extends BaseMSQFault
+{
+ public static final String CODE = "DruidException";
+
+ private final String druidErrorCode;
+ private final String persona;
+ private final String category;
+ private final Map<String, String> context;
+
+ @JsonCreator
+ public DruidExceptionFault(
+ @JsonProperty("druidErrorCode") final String druidErrorCode,
+ @JsonProperty("persona") final String persona,
+ @JsonProperty("category") final String category,
+ @JsonProperty("errorMessage") final String errorMessage,
+ @JsonProperty("context") final Map<String, String> context
+ )
+ {
+ super(CODE, errorMessage);
+ this.druidErrorCode = druidErrorCode;
+ this.persona = persona;
+ this.category = category;
+ this.context = context == null ? Map.of() : context;
+ }
+
+ public static DruidExceptionFault fromDruidException(final DruidException
druidException)
+ {
+ return new DruidExceptionFault(
+ druidException.getErrorCode(),
+ druidException.getTargetPersona().name(),
+ druidException.getCategory().name(),
+ druidException.getMessage(),
+ druidException.getContext()
+ );
+ }
+
+ @JsonProperty
+ public String getDruidErrorCode()
+ {
+ return druidErrorCode;
+ }
+
+ @JsonProperty
+ public String getPersona()
+ {
+ return persona;
+ }
+
+ @JsonProperty
+ public String getCategory()
+ {
+ return category;
+ }
+
+ @JsonProperty
+ @JsonInclude(JsonInclude.Include.NON_EMPTY)
+ public Map<String, String> getContext()
+ {
+ return context;
+ }
+
+ @Override
+ public DruidException toDruidException()
+ {
+ final DruidException.Persona personaEnum =
+ safeValueOf(DruidException.Persona.class, persona,
DruidException.Persona.DEVELOPER);
+ final DruidException.Category categoryEnum =
+ safeValueOf(DruidException.Category.class, category,
DruidException.Category.UNCATEGORIZED);
+ return DruidException.forPersona(personaEnum)
+ .ofCategory(categoryEnum)
+ .withErrorCode(druidErrorCode)
+ .build(getErrorMessage())
+ .withContext(context);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ final DruidExceptionFault that = (DruidExceptionFault) o;
+ return Objects.equals(druidErrorCode, that.druidErrorCode)
+ && Objects.equals(persona, that.persona)
+ && Objects.equals(category, that.category)
+ && Objects.equals(context, that.context);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(super.hashCode(), druidErrorCode, persona, category,
context);
+ }
+
+ /**
+ * Utility function that returns an enum with a particular name, if it
exists, or a default value otherwise.
+ * This exists to make it possible to add new {@link
DruidException.Category} without breaking serde during
+ * rolling updates. Older code attempting to deserialize a new value will
get a default value instead.
+ */
+ private static <T extends Enum<T>> T safeValueOf(final Class<T> enumClass,
final String name, final T defaultValue)
+ {
+ try {
+ return Enum.valueOf(enumClass, name);
+ }
+ catch (IllegalArgumentException e) {
+ return defaultValue;
+ }
+ }
+}
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/DurableStorageConfigurationFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/DurableStorageConfigurationFault.java
index cd65e58c7b7..d1c1787571f 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/DurableStorageConfigurationFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/DurableStorageConfigurationFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import org.apache.druid.msq.guice.MSQDurableStorageModule;
import org.apache.druid.msq.util.MultiStageQueryContext;
@@ -56,6 +57,15 @@ public class DurableStorageConfigurationFault extends
BaseMSQFault
return errorMessage;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.OPERATOR)
+ .ofCategory(DruidException.Category.INVALID_INPUT)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertCannotAllocateSegmentFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertCannotAllocateSegmentFault.java
index f632ae67736..5cbfb40da54 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertCannotAllocateSegmentFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertCannotAllocateSegmentFault.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.google.common.base.Preconditions;
+import org.apache.druid.error.DruidException;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.java.util.common.granularity.DurationGranularity;
import org.apache.druid.java.util.common.granularity.GranularityType;
@@ -118,6 +119,15 @@ public class InsertCannotAllocateSegmentFault extends
BaseMSQFault
}
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.RUNTIME_FAILURE)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertCannotBeEmptyFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertCannotBeEmptyFault.java
index dd2eea9ac7f..83fe3184999 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertCannotBeEmptyFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertCannotBeEmptyFault.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.google.common.base.Preconditions;
+import org.apache.druid.error.DruidException;
import java.util.Objects;
@@ -49,6 +50,15 @@ public class InsertCannotBeEmptyFault extends BaseMSQFault
return dataSource;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.INVALID_INPUT)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertLockPreemptedFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertLockPreemptedFault.java
index c355dc3ff67..0d57bd6f7c1 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertLockPreemptedFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertLockPreemptedFault.java
@@ -21,6 +21,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
@JsonTypeName(InsertLockPreemptedFault.CODE)
public class InsertLockPreemptedFault extends BaseMSQFault
@@ -37,6 +38,15 @@ public class InsertLockPreemptedFault extends BaseMSQFault
);
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.CONFLICT)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@JsonCreator
public static InsertLockPreemptedFault instance()
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertTimeNullFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertTimeNullFault.java
index bcdc592eefc..707b6cebb29 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertTimeNullFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertTimeNullFault.java
@@ -21,6 +21,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import org.apache.druid.segment.column.ColumnHolder;
@JsonTypeName(InsertTimeNullFault.CODE)
@@ -34,6 +35,15 @@ public class InsertTimeNullFault extends BaseMSQFault
super(CODE, "Null timestamp (%s) encountered during INSERT or REPLACE.",
ColumnHolder.TIME_COLUMN_NAME);
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.INVALID_INPUT)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@JsonCreator
public static InsertTimeNullFault instance()
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertTimeOutOfBoundsFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertTimeOutOfBoundsFault.java
index 97638b95708..a433393d9a1 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertTimeOutOfBoundsFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InsertTimeOutOfBoundsFault.java
@@ -21,6 +21,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import org.joda.time.Interval;
import java.util.List;
@@ -63,6 +64,15 @@ public class InsertTimeOutOfBoundsFault extends BaseMSQFault
return intervalBounds;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.INVALID_INPUT)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InvalidFieldFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InvalidFieldFault.java
index 09834fee20f..a96bb5b6846 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InvalidFieldFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InvalidFieldFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import javax.annotation.Nullable;
import java.util.Objects;
@@ -100,6 +101,18 @@ public class InvalidFieldFault extends BaseMSQFault
return logMsg;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ // DEVELOPER since this fault is generated when InvalidFieldException is
used as a wrapper around
+ // miscellaneous exceptions whose meaning is not clear. Underlying code
that has something specific
+ // to say should throw DruidException.
+ return DruidException.forPersona(DruidException.Persona.DEVELOPER)
+ .ofCategory(DruidException.Category.INVALID_INPUT)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InvalidNullByteFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InvalidNullByteFault.java
index 6b88daf7599..44d72b36759 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InvalidNullByteFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/InvalidNullByteFault.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import javax.annotation.Nullable;
import java.util.Objects;
@@ -122,6 +123,15 @@ public class InvalidNullByteFault extends BaseMSQFault
return position;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.INVALID_INPUT)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/MSQErrorReport.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/MSQErrorReport.java
index 8c66280294e..025553961d0 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/MSQErrorReport.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/MSQErrorReport.java
@@ -31,7 +31,6 @@ import org.apache.druid.frame.write.InvalidFieldException;
import org.apache.druid.frame.write.InvalidNullByteException;
import org.apache.druid.frame.write.UnsupportedColumnTypeException;
import org.apache.druid.indexing.common.task.batch.TooManyBucketsException;
-import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.java.util.common.parsers.ParseException;
import
org.apache.druid.query.groupby.epinephelinae.UnexpectedMultiValueDimensionException;
import org.apache.druid.sql.calcite.planner.ColumnMappings;
@@ -227,6 +226,11 @@ public class MSQErrorReport
Throwable cause = e;
+ // Remember the first DruidException we encounter, but keep walking the
cause chain in case a more specific
+ // exception type is nested inside (e.g. when Either.valueOrThrow() wraps
an InvalidNullByteException in a
+ // DruidException).
+ DruidException firstDruidException = null;
+
// This method will grow as we try to add more faults and exceptions
// One way of handling this would be to extend the faults to have a method
like
// public MSQFault fromException(@Nullable Throwable e) which returns the
specific fault if it can be reconstructed
@@ -292,22 +296,31 @@ public class MSQErrorReport
);
} else if (cause instanceof UnexpectedMultiValueDimensionException) {
- return new QueryRuntimeFault(
- StringUtils.format(
- "Column [%s] is a multi-value string. Please wrap the column
using MV_TO_ARRAY() to proceed further.",
- ((UnexpectedMultiValueDimensionException)
cause).getDimensionName()
- ), cause.getMessage()
+ return DruidExceptionFault.fromDruidException(
+ DruidException
+ .forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.RUNTIME_FAILURE)
+ .build(
+ cause,
+ "Column [%s] is a multi-value string. "
+ + "Please wrap the column using MV_TO_ARRAY() to proceed
further.",
+ ((UnexpectedMultiValueDimensionException)
cause).getDimensionName()
+ )
);
} else if (cause instanceof InterruptedException) {
return CanceledFault.unknown();
- } else if
(cause.getClass().getPackage().getName().startsWith("org.apache.druid.query")) {
- // catch all for all query runtime exception faults.
- return new QueryRuntimeFault(e.getMessage(), null);
- } else {
- cause = cause.getCause();
+ } else if (cause instanceof DruidException && firstDruidException ==
null) {
+ firstDruidException = (DruidException) cause;
}
+
+ cause = cause.getCause();
}
- return UnknownFault.forException(e);
+ if (firstDruidException != null) {
+ // Didn't find any more specific exception wrapped underneath the first
DruidException, so go with that one.
+ return DruidExceptionFault.fromDruidException(firstDruidException);
+ } else {
+ return UnknownFault.forException(e);
+ }
}
}
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/MSQFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/MSQFault.java
index 39efce9d204..035a524b5bf 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/MSQFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/MSQFault.java
@@ -39,15 +39,6 @@ public interface MSQFault
/**
* Returns a {@link DruidException} corresponding to this fault.
- *
- * The default is a {@link DruidException.Category#RUNTIME_FAILURE}
targeting {@link DruidException.Persona#USER}.
- * Faults with different personas and categories should override this method.
*/
- default DruidException toDruidException()
- {
- return DruidException.forPersona(DruidException.Persona.USER)
- .ofCategory(DruidException.Category.RUNTIME_FAILURE)
- .withErrorCode(getErrorCode())
-
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
- }
+ DruidException toDruidException();
}
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/NotEnoughMemoryFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/NotEnoughMemoryFault.java
index d4360a09d1a..2859a2daf36 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/NotEnoughMemoryFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/NotEnoughMemoryFault.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import org.apache.druid.java.util.common.StringUtils;
import java.util.Objects;
@@ -130,6 +131,15 @@ public class NotEnoughMemoryFault extends BaseMSQFault
return maxConcurrentStages;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.OPERATOR)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/NotEnoughTemporaryStorageFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/NotEnoughTemporaryStorageFault.java
index 523bde42de4..2a9a6e07b26 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/NotEnoughTemporaryStorageFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/NotEnoughTemporaryStorageFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import java.util.Objects;
@@ -63,6 +64,15 @@ public class NotEnoughTemporaryStorageFault extends
BaseMSQFault
return configuredTemporaryStorage;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.OPERATOR)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/QueryNotSupportedFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/QueryNotSupportedFault.java
index 7356cc02909..ed893931133 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/QueryNotSupportedFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/QueryNotSupportedFault.java
@@ -37,7 +37,7 @@ public class QueryNotSupportedFault extends BaseMSQFault
@Override
public DruidException toDruidException()
{
- return DruidException.forPersona(DruidException.Persona.USER)
+ return DruidException.forPersona(DruidException.Persona.DEVELOPER)
.ofCategory(DruidException.Category.UNSUPPORTED)
.withErrorCode(getErrorCode())
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/QueryRuntimeFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/QueryRuntimeFault.java
index 0950ae46633..b1191593781 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/QueryRuntimeFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/QueryRuntimeFault.java
@@ -22,12 +22,16 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import javax.annotation.Nullable;
import java.util.Objects;
/**
- * Fault to throw when the error comes from the druid native query runtime
while running in the MSQ engine.
+ * Currently unused. Remains for compatibility with older workers that may
still report it, and older reports
+ * that still contain it. Currently, {@link DruidExceptionFault} is preferred
for {@link DruidException} or
+ * recognized exceptions that are thrown by the query stack. Fault {@link
UnknownFault} is preferred for
+ * other errors from the query stack.
*/
@JsonTypeName(QueryRuntimeFault.CODE)
public class QueryRuntimeFault extends BaseMSQFault
@@ -54,6 +58,15 @@ public class QueryRuntimeFault extends BaseMSQFault
return baseErrorMessage;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.DEVELOPER)
+ .ofCategory(DruidException.Category.RUNTIME_FAILURE)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/RowTooLargeFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/RowTooLargeFault.java
index 790e83ceff8..067513cbce8 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/RowTooLargeFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/RowTooLargeFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import org.apache.druid.msq.util.MultiStageQueryContext;
import java.util.Objects;
@@ -52,6 +53,15 @@ public class RowTooLargeFault extends BaseMSQFault
return maxFrameSize;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TaskStartTimeoutFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TaskStartTimeoutFault.java
index 1eba2e8dc77..18b71b30062 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TaskStartTimeoutFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TaskStartTimeoutFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import org.apache.druid.msq.util.MultiStageQueryContext;
import java.util.Objects;
@@ -77,6 +78,15 @@ public class TaskStartTimeoutFault extends BaseMSQFault
return timeout;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.OPERATOR)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyAttemptsForJob.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyAttemptsForJob.java
index a2cb9c3d1fb..13118ea4d6b 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyAttemptsForJob.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyAttemptsForJob.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import java.util.Objects;
@@ -87,6 +88,15 @@ public class TooManyAttemptsForJob extends BaseMSQFault
return rootErrorMessage;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.OPERATOR)
+ .ofCategory(DruidException.Category.RUNTIME_FAILURE)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyAttemptsForWorker.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyAttemptsForWorker.java
index dbf9b607e23..29267f67d03 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyAttemptsForWorker.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyAttemptsForWorker.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import java.util.Objects;
@@ -86,6 +87,15 @@ public class TooManyAttemptsForWorker extends BaseMSQFault
return rootErrorMessage;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.OPERATOR)
+ .ofCategory(DruidException.Category.RUNTIME_FAILURE)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyBucketsFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyBucketsFault.java
index fdad421e649..df70faf8ecd 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyBucketsFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyBucketsFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import java.util.Objects;
@@ -53,6 +54,15 @@ public class TooManyBucketsFault extends BaseMSQFault
return maxBuckets;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyClusteredByColumnsFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyClusteredByColumnsFault.java
index 8fa80082dca..e3ff3b24285 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyClusteredByColumnsFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyClusteredByColumnsFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import java.util.Objects;
@@ -65,6 +66,15 @@ public class TooManyClusteredByColumnsFault extends
BaseMSQFault
return stage;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyColumnsFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyColumnsFault.java
index 05b9d0d197a..d9f68e2d170 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyColumnsFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyColumnsFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import java.util.Objects;
@@ -56,6 +57,15 @@ public class TooManyColumnsFault extends BaseMSQFault
return maxColumns;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyInputFilesFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyInputFilesFault.java
index cae7a5c36d7..dad156566d1 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyInputFilesFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyInputFilesFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import org.apache.druid.msq.util.MultiStageQueryContext;
import java.util.Objects;
@@ -77,6 +78,15 @@ public class TooManyInputFilesFault extends BaseMSQFault
return minNumWorkers;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyPartitionsFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyPartitionsFault.java
index e59b6645f75..f9a6aebb9c3 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyPartitionsFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyPartitionsFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import org.apache.druid.msq.util.MultiStageQueryContext;
import java.util.Objects;
@@ -52,6 +53,15 @@ public class TooManyPartitionsFault extends BaseMSQFault
return maxPartitions;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyRowsInAWindowFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyRowsInAWindowFault.java
index b1905fbb226..0b71505c107 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyRowsInAWindowFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyRowsInAWindowFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import org.apache.druid.msq.util.MultiStageQueryContext;
import java.util.Objects;
@@ -67,6 +68,15 @@ public class TooManyRowsInAWindowFault extends BaseMSQFault
return maxRows;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyRowsWithSameKeyFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyRowsWithSameKeyFault.java
index be284ae502d..3259a1adbef 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyRowsWithSameKeyFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyRowsWithSameKeyFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import java.util.List;
import java.util.Objects;
@@ -74,6 +75,15 @@ public class TooManyRowsWithSameKeyFault extends BaseMSQFault
return maxBytes;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManySegmentsInTimeChunkFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManySegmentsInTimeChunkFault.java
index ac0c2a641da..18e8d272a6d 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManySegmentsInTimeChunkFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManySegmentsInTimeChunkFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import org.apache.druid.java.util.common.granularity.Granularity;
import org.apache.druid.java.util.common.granularity.GranularityType;
import org.apache.druid.msq.util.MultiStageQueryContext;
@@ -103,6 +104,15 @@ public class TooManySegmentsInTimeChunkFault extends
BaseMSQFault
return segmentGranularity;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyWarningsFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyWarningsFault.java
index 57663cb0ddb..0682fda6e2a 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyWarningsFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyWarningsFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import java.util.Objects;
@@ -56,6 +57,15 @@ public class TooManyWarningsFault extends BaseMSQFault
return rootErrorCode;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.RUNTIME_FAILURE)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyWorkersFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyWorkersFault.java
index 42376ca8cb8..cefbe0cfe02 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyWorkersFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/TooManyWorkersFault.java
@@ -22,6 +22,7 @@ package org.apache.druid.msq.indexing.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import java.util.Objects;
@@ -56,6 +57,15 @@ public class TooManyWorkersFault extends BaseMSQFault
return maxWorkers;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.USER)
+ .ofCategory(DruidException.Category.CAPACITY_EXCEEDED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/UnknownFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/UnknownFault.java
index 3a14683613d..a3c8d5f096a 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/UnknownFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/UnknownFault.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import javax.annotation.Nullable;
import java.util.Objects;
@@ -60,6 +61,15 @@ public class UnknownFault extends BaseMSQFault
return message;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.DEVELOPER)
+ .ofCategory(DruidException.Category.UNCATEGORIZED)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/WorkerFailedFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/WorkerFailedFault.java
index f2c579abeb5..a10928261ad 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/WorkerFailedFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/WorkerFailedFault.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import javax.annotation.Nullable;
import java.util.Objects;
@@ -62,6 +63,15 @@ public class WorkerFailedFault extends BaseMSQFault
return errorMsg;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.OPERATOR)
+ .ofCategory(DruidException.Category.RUNTIME_FAILURE)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/WorkerRpcFailedFault.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/WorkerRpcFailedFault.java
index 9754d0ed840..a5cfe113299 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/WorkerRpcFailedFault.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/error/WorkerRpcFailedFault.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
+import org.apache.druid.error.DruidException;
import javax.annotation.Nullable;
import java.util.Objects;
@@ -60,6 +61,15 @@ public class WorkerRpcFailedFault extends BaseMSQFault
return errorMsg;
}
+ @Override
+ public DruidException toDruidException()
+ {
+ return DruidException.forPersona(DruidException.Persona.OPERATOR)
+ .ofCategory(DruidException.Category.RUNTIME_FAILURE)
+ .withErrorCode(getErrorCode())
+
.build(MSQFaultUtils.generateMessageWithErrorCode(this));
+ }
+
@Override
public boolean equals(Object o)
{
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/processor/SegmentGeneratorFrameProcessor.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/processor/SegmentGeneratorFrameProcessor.java
index d42ce9455fd..e37c755bdd5 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/processor/SegmentGeneratorFrameProcessor.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/indexing/processor/SegmentGeneratorFrameProcessor.java
@@ -19,6 +19,7 @@
package org.apache.druid.msq.indexing.processor;
+import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ListenableFuture;
import it.unimi.dsi.fastutil.ints.IntSet;
@@ -208,6 +209,7 @@ public class SegmentGeneratorFrameProcessor implements
FrameProcessor<DataSegmen
segmentGenerationProgressCounter.incrementRowsProcessed(1);
}
catch (Exception e) {
+ Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
}
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/input/RegularLoadableSegment.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/input/RegularLoadableSegment.java
index 33399e354a1..7582cd2514c 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/input/RegularLoadableSegment.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/input/RegularLoadableSegment.java
@@ -225,7 +225,7 @@ public class RegularLoadableSegment implements
LoadableSegment
*/
private DruidException segmentNotFound()
{
- return DruidException.forPersona(DruidException.Persona.USER)
+ return DruidException.forPersona(DruidException.Persona.OPERATOR)
.ofCategory(DruidException.Category.RUNTIME_FAILURE)
.build("Segment[%s] not found on this server. Please
retry your query.", segmentId);
}
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/querykit/results/ExportResultsFrameProcessor.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/querykit/results/ExportResultsFrameProcessor.java
index 9c9a9028892..7c5558791f9 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/querykit/results/ExportResultsFrameProcessor.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/querykit/results/ExportResultsFrameProcessor.java
@@ -197,7 +197,7 @@ public class ExportResultsFrameProcessor implements
FrameProcessor<Object>
}
}
catch (IOException e) {
- throw DruidException.forPersona(DruidException.Persona.USER)
+ throw DruidException.forPersona(DruidException.Persona.OPERATOR)
.ofCategory(DruidException.Category.RUNTIME_FAILURE)
.build(
e,
@@ -223,7 +223,7 @@ public class ExportResultsFrameProcessor implements
FrameProcessor<Object>
if (stream != null) {
stream.close();
}
- throw DruidException.forPersona(DruidException.Persona.USER)
+ throw DruidException.forPersona(DruidException.Persona.OPERATOR)
.ofCategory(DruidException.Category.RUNTIME_FAILURE)
.build(e, "Exception occurred while opening a stream
to the export location [%s].", exportFilePath);
}
diff --git
a/multi-stage-query/src/main/java/org/apache/druid/msq/sql/resources/SqlStatementResource.java
b/multi-stage-query/src/main/java/org/apache/druid/msq/sql/resources/SqlStatementResource.java
index 06adbc74f19..b71d9d2221d 100644
---
a/multi-stage-query/src/main/java/org/apache/druid/msq/sql/resources/SqlStatementResource.java
+++
b/multi-stage-query/src/main/java/org/apache/druid/msq/sql/resources/SqlStatementResource.java
@@ -988,7 +988,7 @@ public class SqlStatementResource
private void checkForDurableStorageConnectorImpl()
{
if (storageConnector instanceof NilStorageConnector) {
- throw DruidException.forPersona(DruidException.Persona.USER)
+ throw DruidException.forPersona(DruidException.Persona.OPERATOR)
.ofCategory(DruidException.Category.INVALID_INPUT)
.build(
StringUtils.format(
diff --git
a/multi-stage-query/src/test/java/org/apache/druid/msq/dart/controller/http/DartSqlResourceTest.java
b/multi-stage-query/src/test/java/org/apache/druid/msq/dart/controller/http/DartSqlResourceTest.java
index 1b2d7d8a547..03ebec54087 100644
---
a/multi-stage-query/src/test/java/org/apache/druid/msq/dart/controller/http/DartSqlResourceTest.java
+++
b/multi-stage-query/src/test/java/org/apache/druid/msq/dart/controller/http/DartSqlResourceTest.java
@@ -581,7 +581,7 @@ public class DartSqlResourceTest extends MSQTestBase
);
Assertions.assertNull(sqlResource.doPost(sqlQuery, httpServletRequest));
-
Assertions.assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
asyncResponse.getStatus());
+ Assertions.assertEquals(Response.Status.BAD_REQUEST.getStatusCode(),
asyncResponse.getStatus());
final Map<String, Object> e = objectMapper.readValue(
asyncResponse.baos.toByteArray(),
@@ -589,7 +589,7 @@ public class DartSqlResourceTest extends MSQTestBase
);
Assertions.assertEquals("InvalidNullByte", e.get("errorCode"));
- Assertions.assertEquals("RUNTIME_FAILURE", e.get("category"));
+ Assertions.assertEquals("INVALID_INPUT", e.get("category"));
assertThat((String) e.get("errorMessage"),
CoreMatchers.startsWith("InvalidNullByte: "));
}
diff --git
a/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQFaultsTest.java
b/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQFaultsTest.java
index f83b86be77c..b74c76ee766 100644
---
a/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQFaultsTest.java
+++
b/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQFaultsTest.java
@@ -34,6 +34,7 @@ import org.apache.druid.java.util.common.ISE;
import org.apache.druid.java.util.common.Intervals;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.java.util.common.granularity.Granularities;
+import org.apache.druid.msq.indexing.error.DruidExceptionFault;
import org.apache.druid.msq.indexing.error.InsertCannotAllocateSegmentFault;
import org.apache.druid.msq.indexing.error.InsertCannotBeEmptyFault;
import org.apache.druid.msq.indexing.error.InsertTimeNullFault;
@@ -711,6 +712,32 @@ public class MSQFaultsTest extends MSQTestBase
.verifyExecutionError();
}
+ @Test
+ public void testDruidExceptionFault()
+ {
+ // BITWISE_COMPLEMENT(m1 * 1e19) throws because the double value exceeds
Long range. The expression engine
+ // wraps this in DruidException, which getFaultFromException() converts to
DruidExceptionFault.
+ final Map<String, Object> context = ImmutableMap.<String, Object>builder()
+
.putAll(DEFAULT_MSQ_CONTEXT)
+ .put("vectorize", "false")
+ .build();
+
+ testSelectQuery()
+ .setSql("SELECT BITWISE_COMPLEMENT(m1 * 1e19) FROM foo")
+ .setQueryContext(context)
+ .setExpectedMSQFault(
+ new DruidExceptionFault(
+ "general",
+ "USER",
+ "INVALID_INPUT",
+ "Function[bitwiseComplement] Possible data truncation, param
[10000000000000000000.000000]"
+ + " is out of LONG value range",
+ Collections.emptyMap()
+ )
+ )
+ .verifyResults();
+ }
+
private void testLockTypes(TaskLockType contextTaskLockType, String sql,
String errorMessage)
{
Map<String, Object> context = new HashMap<>(DEFAULT_MSQ_CONTEXT);
diff --git
a/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQSelectTest.java
b/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQSelectTest.java
index 509e93333a8..7b16ff41e75 100644
---
a/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQSelectTest.java
+++
b/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQSelectTest.java
@@ -2808,9 +2808,7 @@ public class MSQSelectTest extends MSQTestBase
.setExpectedMSQFault(CanceledFault.timeout())
.setExpectedExecutionErrorMatcher(CoreMatchers.allOf(
CoreMatchers.instanceOf(ISE.class),
- ThrowableMessageMatcher.hasMessage(CoreMatchers.containsString(
- " Query canceled due to [Configured query timeout].")
- )
+
ThrowableMessageMatcher.hasMessage(CoreMatchers.containsString("Query timed
out"))
))
.verifyExecutionError();
}
diff --git
a/multi-stage-query/src/test/java/org/apache/druid/msq/indexing/error/MSQErrorReportTest.java
b/multi-stage-query/src/test/java/org/apache/druid/msq/indexing/error/MSQErrorReportTest.java
index 0570195cdee..f7641938f3c 100644
---
a/multi-stage-query/src/test/java/org/apache/druid/msq/indexing/error/MSQErrorReportTest.java
+++
b/multi-stage-query/src/test/java/org/apache/druid/msq/indexing/error/MSQErrorReportTest.java
@@ -60,11 +60,13 @@ public class MSQErrorReportTest
Assert.assertEquals(new RowTooLargeFault(10),
MSQErrorReport.getFaultFromException(tooLargeException));
UnexpectedMultiValueDimensionException mvException = new
UnexpectedMultiValueDimensionException(ERROR_MESSAGE);
- Assert.assertEquals(QueryRuntimeFault.CODE,
MSQErrorReport.getFaultFromException(mvException).getErrorCode());
+ Assert.assertEquals(DruidExceptionFault.CODE,
MSQErrorReport.getFaultFromException(mvException).getErrorCode());
+ // QueryTimeoutException (legacy QueryException, not a DruidException)
becomes an UnknownFault.
+ // Only DruidExceptions become nice faults.
QueryTimeoutException queryException = new
QueryTimeoutException(ERROR_MESSAGE);
Assert.assertEquals(
- new QueryRuntimeFault(ERROR_MESSAGE, null),
+ UnknownFault.forException(queryException),
MSQErrorReport.getFaultFromException(queryException)
);
diff --git
a/multi-stage-query/src/test/java/org/apache/druid/msq/indexing/error/MSQFaultSerdeTest.java
b/multi-stage-query/src/test/java/org/apache/druid/msq/indexing/error/MSQFaultSerdeTest.java
index 594b7bf244d..e4ef013a515 100644
---
a/multi-stage-query/src/test/java/org/apache/druid/msq/indexing/error/MSQFaultSerdeTest.java
+++
b/multi-stage-query/src/test/java/org/apache/druid/msq/indexing/error/MSQFaultSerdeTest.java
@@ -60,6 +60,20 @@ public class MSQFaultSerdeTest
assertFaultSerde(new ColumnTypeNotSupportedFault("the column", null));
assertFaultSerde(new ColumnTypeNotSupportedFault("the column",
ColumnType.STRING_ARRAY));
assertFaultSerde(new ColumnNameRestrictedFault("the column"));
+ assertFaultSerde(new DruidExceptionFault(
+ "general",
+ "USER",
+ "INVALID_INPUT",
+ "test message",
+ Collections.emptyMap()
+ ));
+ assertFaultSerde(new DruidExceptionFault(
+ "custom",
+ "ADMIN",
+ "RUNTIME_FAILURE",
+ "another message",
+ Collections.singletonMap("key", "value")
+ ));
assertFaultSerde(new InsertCannotAllocateSegmentFault("the datasource",
Intervals.ETERNITY, null));
assertFaultSerde(new InsertCannotAllocateSegmentFault(
"the datasource",
diff --git
a/multi-stage-query/src/test/java/org/apache/druid/msq/indexing/error/MSQFaultToDruidExceptionTest.java
b/multi-stage-query/src/test/java/org/apache/druid/msq/indexing/error/MSQFaultToDruidExceptionTest.java
new file mode 100644
index 00000000000..5391ec21850
--- /dev/null
+++
b/multi-stage-query/src/test/java/org/apache/druid/msq/indexing/error/MSQFaultToDruidExceptionTest.java
@@ -0,0 +1,411 @@
+/*
+ * 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.msq.indexing.error;
+
+import org.apache.druid.error.DruidException;
+import org.apache.druid.java.util.common.DateTimes;
+import org.apache.druid.java.util.common.Intervals;
+import org.apache.druid.java.util.common.granularity.Granularities;
+import org.apache.druid.query.JoinAlgorithm;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+public class MSQFaultToDruidExceptionTest
+{
+ @Test
+ public void testBroadcastTablesTooLargeFault()
+ {
+ final DruidException e = new BroadcastTablesTooLargeFault(10,
JoinAlgorithm.SORT_MERGE).toDruidException();
+ Assert.assertEquals("BroadcastTablesTooLarge", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testCanceledFaultShutdown()
+ {
+ final DruidException e = CanceledFault.shutdown().toDruidException();
+ Assert.assertEquals("Canceled", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CANCELED, e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testCanceledFaultTimeout()
+ {
+ final DruidException e = CanceledFault.timeout().toDruidException();
+ Assert.assertEquals("Canceled", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.TIMEOUT, e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testCanceledFaultUserRequest()
+ {
+ final DruidException e = CanceledFault.userRequest().toDruidException();
+ Assert.assertEquals("Canceled", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CANCELED, e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testCanceledFaultUnknown()
+ {
+ final DruidException e = CanceledFault.unknown().toDruidException();
+ Assert.assertEquals("Canceled", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CANCELED, e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testCannotParseExternalDataFault()
+ {
+ final DruidException e = new CannotParseExternalDataFault("the
message").toDruidException();
+ Assert.assertEquals("CannotParseExternalData", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.INVALID_INPUT,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testDruidExceptionFault()
+ {
+ final DruidException e = new DruidExceptionFault(
+ "custom",
+ "USER",
+ "RUNTIME_FAILURE",
+ "test message",
+ Collections.singletonMap("key", "value")
+ ).toDruidException();
+ Assert.assertEquals("custom", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.RUNTIME_FAILURE,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ Assert.assertEquals(Collections.singletonMap("key", "value"),
e.getContext());
+ }
+
+ @Test
+ public void testDruidExceptionFaultUnknownEnums()
+ {
+ final DruidException e = new DruidExceptionFault(
+ "custom",
+ "NONEXISTENT_PERSONA",
+ "NONEXISTENT_CATEGORY",
+ "test message",
+ Collections.emptyMap()
+ ).toDruidException();
+ Assert.assertEquals("custom", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.UNCATEGORIZED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.DEVELOPER,
e.getTargetPersona());
+ }
+
+ @Test
+ public void testDurableStorageConfigurationFault()
+ {
+ final DruidException e = new DurableStorageConfigurationFault("some
error").toDruidException();
+ Assert.assertEquals("DurableStorageConfiguration", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.INVALID_INPUT,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.OPERATOR, e.getTargetPersona());
+ }
+
+ @Test
+ public void testInsertCannotAllocateSegmentFault()
+ {
+ final DruidException e = new InsertCannotAllocateSegmentFault(
+ "the datasource",
+ Intervals.ETERNITY,
+ null
+ ).toDruidException();
+ Assert.assertEquals("InsertCannotAllocateSegment", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.RUNTIME_FAILURE,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testInsertCannotBeEmptyFault()
+ {
+ final DruidException e = new InsertCannotBeEmptyFault("the
datasource").toDruidException();
+ Assert.assertEquals("InsertCannotBeEmpty", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.INVALID_INPUT,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testInsertLockPreemptedFault()
+ {
+ final DruidException e =
InsertLockPreemptedFault.INSTANCE.toDruidException();
+ Assert.assertEquals("InsertLockPreempted", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CONFLICT, e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testInsertTimeNullFault()
+ {
+ final DruidException e = InsertTimeNullFault.INSTANCE.toDruidException();
+ Assert.assertEquals("InsertTimeNull", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.INVALID_INPUT,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testInsertTimeOutOfBoundsFault()
+ {
+ final DruidException e = new InsertTimeOutOfBoundsFault(
+ Intervals.of("2001/2002"),
+ Collections.singletonList(Intervals.of("2000/2001"))
+ ).toDruidException();
+ Assert.assertEquals("InsertTimeOutOfBounds", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.INVALID_INPUT,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testInvalidFieldFault()
+ {
+ final DruidException e = new InvalidFieldFault(
+ "the source",
+ "the column",
+ 1,
+ "the error",
+ "the log msg"
+ ).toDruidException();
+ Assert.assertEquals("InvalidField", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.INVALID_INPUT,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.DEVELOPER,
e.getTargetPersona());
+ }
+
+ @Test
+ public void testInvalidNullByteFault()
+ {
+ final DruidException e = new InvalidNullByteFault(
+ "the source",
+ 1,
+ "the column",
+ "the value",
+ 2
+ ).toDruidException();
+ Assert.assertEquals("InvalidNullByte", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.INVALID_INPUT,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testNotEnoughMemoryFault()
+ {
+ final DruidException e = new NotEnoughMemoryFault(
+ 1234,
+ 1000,
+ 1000,
+ 900,
+ 1,
+ 2,
+ 2
+ ).toDruidException();
+ Assert.assertEquals("NotEnoughMemory", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.OPERATOR, e.getTargetPersona());
+ }
+
+ @Test
+ public void testNotEnoughTemporaryStorageFault()
+ {
+ final DruidException e = new NotEnoughTemporaryStorageFault(250,
2).toDruidException();
+ Assert.assertEquals("NotEnoughTemporaryStorageFault", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.OPERATOR, e.getTargetPersona());
+ }
+
+ @Test
+ public void testQueryNotSupportedFault()
+ {
+ final DruidException e =
QueryNotSupportedFault.INSTANCE.toDruidException();
+ Assert.assertEquals("QueryNotSupported", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.UNSUPPORTED, e.getCategory());
+ Assert.assertEquals(DruidException.Persona.DEVELOPER,
e.getTargetPersona());
+ }
+
+ @Test
+ public void testQueryRuntimeFault()
+ {
+ final DruidException e = new QueryRuntimeFault("new error", "base
error").toDruidException();
+ Assert.assertEquals("QueryRuntimeError", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.RUNTIME_FAILURE,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.DEVELOPER,
e.getTargetPersona());
+ }
+
+ @Test
+ public void testRowTooLargeFault()
+ {
+ final DruidException e = new RowTooLargeFault(1000).toDruidException();
+ Assert.assertEquals("RowTooLarge", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTaskStartTimeoutFault()
+ {
+ final DruidException e = new TaskStartTimeoutFault(1, 10,
11).toDruidException();
+ Assert.assertEquals("TaskStartTimeout", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.OPERATOR, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTooManyAttemptsForJob()
+ {
+ final DruidException e = new TooManyAttemptsForJob(2, 2, "taskId",
"rootError").toDruidException();
+ Assert.assertEquals("TooManyAttemptsForJob", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.RUNTIME_FAILURE,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.OPERATOR, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTooManyAttemptsForWorker()
+ {
+ final DruidException e = new TooManyAttemptsForWorker(2, "taskId", 1,
"rootError").toDruidException();
+ Assert.assertEquals("TooManyAttemptsForWorker", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.RUNTIME_FAILURE,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.OPERATOR, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTooManyBucketsFault()
+ {
+ final DruidException e = new TooManyBucketsFault(10).toDruidException();
+ Assert.assertEquals("TooManyBuckets", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTooManyClusteredByColumnsFault()
+ {
+ final DruidException e = new TooManyClusteredByColumnsFault(10, 8,
1).toDruidException();
+ Assert.assertEquals("TooManyClusteredByColumns", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTooManyColumnsFault()
+ {
+ final DruidException e = new TooManyColumnsFault(10, 8).toDruidException();
+ Assert.assertEquals("TooManyColumns", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTooManyInputFilesFault()
+ {
+ final DruidException e = new TooManyInputFilesFault(15, 10,
5).toDruidException();
+ Assert.assertEquals("TooManyInputFiles", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTooManyPartitionsFault()
+ {
+ final DruidException e = new TooManyPartitionsFault(10).toDruidException();
+ Assert.assertEquals("TooManyPartitions", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTooManyRowsInAWindowFault()
+ {
+ final DruidException e = new TooManyRowsInAWindowFault(10,
20).toDruidException();
+ Assert.assertEquals("TooManyRowsInAWindow", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTooManyRowsWithSameKeyFault()
+ {
+ final DruidException e = new TooManyRowsWithSameKeyFault(
+ Arrays.asList("foo", 123), 1, 2
+ ).toDruidException();
+ Assert.assertEquals("TooManyRowsWithSameKey", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTooManySegmentsInTimeChunkFault()
+ {
+ final DruidException e = new TooManySegmentsInTimeChunkFault(
+ DateTimes.nowUtc(), 10, 1, Granularities.ALL
+ ).toDruidException();
+ Assert.assertEquals("TooManySegmentsInTimeChunk", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTooManyWarningsFault()
+ {
+ final DruidException e = new TooManyWarningsFault(10, "the
error").toDruidException();
+ Assert.assertEquals("TooManyWarnings", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.RUNTIME_FAILURE,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testTooManyWorkersFault()
+ {
+ final DruidException e = new TooManyWorkersFault(10, 5).toDruidException();
+ Assert.assertEquals("TooManyWorkers", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.CAPACITY_EXCEEDED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.USER, e.getTargetPersona());
+ }
+
+ @Test
+ public void testUnknownFault()
+ {
+ final DruidException e = UnknownFault.forMessage("the
message").toDruidException();
+ Assert.assertEquals("UnknownError", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.UNCATEGORIZED,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.DEVELOPER,
e.getTargetPersona());
+ }
+
+ @Test
+ public void testWorkerFailedFault()
+ {
+ final DruidException e = new WorkerFailedFault("the worker task", "the
error msg").toDruidException();
+ Assert.assertEquals("WorkerFailed", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.RUNTIME_FAILURE,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.OPERATOR, e.getTargetPersona());
+ }
+
+ @Test
+ public void testWorkerRpcFailedFault()
+ {
+ final DruidException e = new WorkerRpcFailedFault("the worker task", "the
error msg").toDruidException();
+ Assert.assertEquals("WorkerRpcFailed", e.getErrorCode());
+ Assert.assertEquals(DruidException.Category.RUNTIME_FAILURE,
e.getCategory());
+ Assert.assertEquals(DruidException.Persona.OPERATOR, e.getTargetPersona());
+ }
+}
diff --git
a/processing/src/main/java/org/apache/druid/frame/write/RowBasedFrameWriter.java
b/processing/src/main/java/org/apache/druid/frame/write/RowBasedFrameWriter.java
index aeb9beadba0..7af90bd82b9 100644
---
a/processing/src/main/java/org/apache/druid/frame/write/RowBasedFrameWriter.java
+++
b/processing/src/main/java/org/apache/druid/frame/write/RowBasedFrameWriter.java
@@ -19,7 +19,6 @@
package org.apache.druid.frame.write;
-import com.google.common.base.Throwables;
import com.google.common.primitives.Ints;
import org.apache.datasketches.memory.Memory;
import org.apache.datasketches.memory.WritableMemory;
@@ -304,8 +303,8 @@ public class RowBasedFrameWriter implements FrameWriter
.column(signature.getColumnName(i))
.build();
}
- catch (ParseException pe) {
- throw Throwables.propagate(pe);
+ catch (DruidException | ParseException e) {
+ throw e;
}
catch (Exception e) {
throw
InvalidFieldException.builder().column(signature.getColumnName(i))
diff --git
a/processing/src/main/java/org/apache/druid/java/util/common/Either.java
b/processing/src/main/java/org/apache/druid/java/util/common/Either.java
index 5a2720d0748..a9c0072fe4a 100644
--- a/processing/src/main/java/org/apache/druid/java/util/common/Either.java
+++ b/processing/src/main/java/org/apache/druid/java/util/common/Either.java
@@ -20,6 +20,7 @@
package org.apache.druid.java.util.common;
import com.google.common.base.Preconditions;
+import org.apache.druid.error.DruidException;
import javax.annotation.Nullable;
import java.util.Objects;
@@ -79,8 +80,9 @@ public class Either<L, R>
/**
* If this Either represents a value, returns it. If this Either represents
an error, throw an error.
*
- * If the error is a {@link Throwable}, it is wrapped in a RuntimeException
and thrown. If it is not a throwable,
- * a generic error is thrown containing the string representation of the
error object.
+ * If the error is a {@link DruidException}, it is thrown. If it is some
other {@link Throwable}, it is
+ * wrapped in a {@link DruidException} and thrown. If it is not a throwable,
a generic {@link DruidException}
+ * is thrown containing the string representation of the error object.
*
* To retrieve the error as-is, use {@link #isError()} and {@link #error()}
instead.
*/
@@ -90,11 +92,22 @@ public class Either<L, R>
if (isValue()) {
return value;
} else if (error instanceof Throwable) {
- // Always wrap Throwable, even if we could throw it directly, to provide
additional context
- // about where the exception happened (we want the current stack frame
in the trace).
- throw new RuntimeException((Throwable) error);
+ // The exception happened somewhere else, perhaps in another thread
entirely. If it is a DruidException
+ // targeting a non-DEVELOPER persona, re-throw it as-is so we keep the
original intent of the error message.
+ // Otherwise, wrap it in a DEVELOPER-oriented DruidException so the
stack trace of the current thread is
+ // added to the exception details.
+ if (error instanceof DruidException
+ && ((DruidException) error).getTargetPersona() !=
DruidException.Persona.DEVELOPER) {
+ throw (DruidException) error;
+ }
+
+ throw DruidException.forPersona(DruidException.Persona.DEVELOPER)
+ .ofCategory(DruidException.Category.UNCATEGORIZED)
+ .build((Throwable) error, ((Throwable)
error).getMessage());
} else {
- throw new RuntimeException(error.toString());
+ throw DruidException.forPersona(DruidException.Persona.DEVELOPER)
+ .ofCategory(DruidException.Category.UNCATEGORIZED)
+ .build("%s", error);
}
}
diff --git a/processing/src/test/java/org/apache/druid/common/EitherTest.java
b/processing/src/test/java/org/apache/druid/common/EitherTest.java
index bb081a24242..a440e9676fc 100644
--- a/processing/src/test/java/org/apache/druid/common/EitherTest.java
+++ b/processing/src/test/java/org/apache/druid/common/EitherTest.java
@@ -20,6 +20,7 @@
package org.apache.druid.common;
import nl.jqno.equalsverifier.EqualsVerifier;
+import org.apache.druid.error.DruidException;
import org.apache.druid.java.util.common.Either;
import org.apache.druid.java.util.common.StringUtils;
import org.hamcrest.CoreMatchers;
@@ -104,6 +105,34 @@ public class EitherTest
Assert.assertEquals("Error[java.lang.AssertionError: oh no]",
either.toString());
}
+ @Test
+ public void testErrorDruidExceptionUserPersona()
+ {
+ final DruidException original =
DruidException.forPersona(DruidException.Persona.USER)
+
.ofCategory(DruidException.Category.INVALID_INPUT)
+ .build("bad input");
+ final Either<DruidException, Object> either = Either.error(original);
+
+ // Non-DEVELOPER DruidExceptions are re-thrown as-is by valueOrThrow.
+ final DruidException e = Assert.assertThrows(DruidException.class,
either::valueOrThrow);
+ Assert.assertSame(original, e);
+ }
+
+ @Test
+ public void testErrorDruidExceptionDeveloperPersona()
+ {
+ final DruidException original =
DruidException.forPersona(DruidException.Persona.DEVELOPER)
+
.ofCategory(DruidException.Category.UNCATEGORIZED)
+ .build("internal error");
+ final Either<DruidException, Object> either = Either.error(original);
+
+ // DEVELOPER DruidExceptions are wrapped to capture the current stack
trace.
+ final DruidException e = Assert.assertThrows(DruidException.class,
either::valueOrThrow);
+ Assert.assertNotSame(original, e);
+ Assert.assertSame(original, e.getCause());
+ Assert.assertEquals(DruidException.Persona.DEVELOPER,
e.getTargetPersona());
+ }
+
@Test
public void testEqualsAndHashCode()
{
diff --git
a/quidem-ut/src/test/quidem/org.apache.druid.quidem.QTest/qaJsonCols/funcs_and_sql_func_json_keys.03.dart.iq
b/quidem-ut/src/test/quidem/org.apache.druid.quidem.QTest/qaJsonCols/funcs_and_sql_func_json_keys.03.dart.iq
index 5e666556a32..d67c29642a9 100644
---
a/quidem-ut/src/test/quidem/org.apache.druid.quidem.QTest/qaJsonCols/funcs_and_sql_func_json_keys.03.dart.iq
+++
b/quidem-ut/src/test/quidem/org.apache.druid.quidem.QTest/qaJsonCols/funcs_and_sql_func_json_keys.03.dart.iq
@@ -371,7 +371,7 @@ WHERE json_keys(c1, '$.a_obj.a_num_int') = json_keys(c2,
'$.a_obj.a_num_int');
SELECT count(*) c
FROM test_json_cols t1
INNER JOIN test_json_cols t2 ON json_keys(t1.c1, '$.a_obj')=json_keys(t2.c1,
'$.a_obj');
-QueryUnsupportedException
+Joining against ARRAY columns is not supported
!error
#-------------------------------------------------------------------------
@@ -380,7 +380,7 @@ QueryUnsupportedException
SELECT count(*) c
FROM test_json_cols t1
INNER JOIN test_json_cols t2 ON json_keys(t1.c1,
'$.a_obj.a_str')=json_keys(t2.c1, '$.a_obj.a_str');
-QueryUnsupportedException
+Joining against ARRAY columns is not supported
!error
#-------------------------------------------------------------------------
@@ -389,7 +389,7 @@ QueryUnsupportedException
SELECT count(*) c
FROM test_json_cols t1
INNER JOIN test_json_cols t2 ON json_keys(t1.c1,
'$.a_obj.a_num_int')=json_keys(t2.c1, '$.a_obj.a_num_int');
-QueryUnsupportedException
+Joining against ARRAY columns is not supported
!error
#-------------------------------------------------------------------------
@@ -398,7 +398,7 @@ QueryUnsupportedException
SELECT count(*) c
FROM test_json_cols t1
LEFT JOIN test_json_cols t2 ON json_keys(t1.c1, '$.a_obj')=json_keys(t2.c1,
'$.a_obj');
-QueryUnsupportedException
+Joining against ARRAY columns is not supported
!error
#-------------------------------------------------------------------------
@@ -407,7 +407,7 @@ QueryUnsupportedException
SELECT count(*) c
FROM test_json_cols t1
LEFT JOIN test_json_cols t2 ON json_keys(t1.c1,
'$.a_obj.a_str')=json_keys(t2.c1, '$.a_obj.a_str');
-QueryUnsupportedException
+Joining against ARRAY columns is not supported
!error
#-------------------------------------------------------------------------
@@ -416,7 +416,7 @@ QueryUnsupportedException
SELECT count(*) c
FROM test_json_cols t1
LEFT JOIN test_json_cols t2 ON json_keys(t1.c1,
'$.a_obj.a_num_int')=json_keys(t2.c1, '$.a_obj.a_num_int');
-QueryUnsupportedException
+Joining against ARRAY columns is not supported
!error
#-------------------------------------------------------------------------
@@ -426,7 +426,7 @@ SELECT count(*) c
FROM test_json_cols t1,
test_json_cols t2
WHERE json_keys(t1.c1, '$.a_obj')=json_keys(t2.c1, '$.a_obj');
-QueryUnsupportedException
+Joining against ARRAY columns is not supported
!error
#-------------------------------------------------------------------------
@@ -436,7 +436,7 @@ SELECT count(*) c
FROM test_json_cols t1,
test_json_cols t2
WHERE json_keys(t1.c1, '$.a_obj.a_str')=json_keys(t2.c1, '$.a_obj.a_str');
-QueryUnsupportedException
+Joining against ARRAY columns is not supported
!error
#-------------------------------------------------------------------------
@@ -446,7 +446,7 @@ SELECT count(*) c
FROM test_json_cols t1,
test_json_cols t2
WHERE json_keys(t1.c1, '$.a_obj.a_num_int')=json_keys(t2.c1,
'$.a_obj.a_num_int');
-QueryUnsupportedException
+Joining against ARRAY columns is not supported
!error
#-------------------------------------------------------------------------
diff --git
a/quidem-ut/src/test/quidem/org.apache.druid.quidem.QTest/qaJsonCols/funcs_and_sql_func_json_paths.02.dart.iq
b/quidem-ut/src/test/quidem/org.apache.druid.quidem.QTest/qaJsonCols/funcs_and_sql_func_json_paths.02.dart.iq
index 530d91cb68f..895b750ad88 100644
---
a/quidem-ut/src/test/quidem/org.apache.druid.quidem.QTest/qaJsonCols/funcs_and_sql_func_json_paths.02.dart.iq
+++
b/quidem-ut/src/test/quidem/org.apache.druid.quidem.QTest/qaJsonCols/funcs_and_sql_func_json_paths.02.dart.iq
@@ -732,7 +732,7 @@ WHERE json_paths(c1) = json_paths(c2);
SELECT count(*) c
FROM test_json_cols t1
INNER JOIN test_json_cols t2 ON json_paths(t1.c1)=json_paths(t2.c1);
-QueryUnsupportedException
+Joining against ARRAY columns is not supported
!error
#-------------------------------------------------------------------------
@@ -741,7 +741,7 @@ QueryUnsupportedException
SELECT count(*) c
FROM test_json_cols t1
LEFT JOIN test_json_cols t2 ON json_paths(t1.c1)=json_paths(t2.c1);
-QueryUnsupportedException
+Joining against ARRAY columns is not supported
!error
#-------------------------------------------------------------------------
@@ -751,7 +751,7 @@ SELECT count(*) c
FROM test_json_cols t1,
test_json_cols t2
WHERE json_paths(t1.c1)=json_paths(t2.c1);
-QueryUnsupportedException
+Joining against ARRAY columns is not supported
!error
#-------------------------------------------------------------------------
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]