Add table and column information to query errors#14388
Add table and column information to query errors#14388bziobrowski wants to merge 3 commits intoapache:masterfrom
Conversation
| + " LIMIT 200 " | ||
| + ") " | ||
| + " GROUP BY time_col "; | ||
|
|
There was a problem hiding this comment.
I think it might be better to add test function to registry but make it work only if a system flag is set, e.g. enableTestFunctions, than to rely on fragile mocks.
| * under the License. | ||
| */ | ||
| package org.apache.pinot.core.common; | ||
|
|
There was a problem hiding this comment.
This one's a just a simple exception that makes it possible to fetch important context information spread throught the stack.
I'd be good to discuss how to integrate the approach with other exceptions and re-organize exceptions thrown by the engine. There are many more places that don't report conversion errors properly (e.g. plenty of parse calls for function arguments that won't report which argument they failed for).
There was a problem hiding this comment.
I think this is a really nice improvement! IMO we could add similar custom runtime exceptions for different classes of processing errors as a subsequent enhancement. Although in that case it might be nice to name this class something more specific than PinotRuntimeException, WDYT?
There was a problem hiding this comment.
I completely agree we need to have our own family of exceptions, but I think it will need more discussion.
My initial suggestion is to reserve PinotRuntimeException name for a marker exception that extends RuntimeException, then add another for queries (something like QueryException) and then
---
config:
look: handDrawn
theme: neutral
---
classDiagram
RuntimeException
note for RuntimeException "From JDK"
PinotRuntimeException
note for PinotRuntimeException "All new Pinot exceptions"
QException
note for QException "All new Pinot exceptions"
QException <|-- ParsingQException
QException <|-- OptimizationQException
ExecutionQException
note for ExecutionQException "Exceptions thrown by Operators"
RuntimeException <|-- PinotRuntimeException
PinotRuntimeException <|-- QException
QException <|-- ExecutionQException
ExecutionQException <|-- ColumnExecutionQException
class ExecutionQException {
+stage
}
class ColumnExecutionQException {
+table
+column
}
note for ColumnExecutionQException "Execution exception dealing with a column"
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #14388 +/- ##
============================================
+ Coverage 61.75% 63.76% +2.01%
- Complexity 207 1555 +1348
============================================
Files 2436 2662 +226
Lines 133233 146118 +12885
Branches 20636 22350 +1714
============================================
+ Hits 82274 93173 +10899
- Misses 44911 46058 +1147
- Partials 6048 6887 +839
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| * under the License. | ||
| */ | ||
| package org.apache.pinot.core.common; | ||
|
|
There was a problem hiding this comment.
I think this is a really nice improvement! IMO we could add similar custom runtime exceptions for different classes of processing errors as a subsequent enhancement. Although in that case it might be nice to name this class something more specific than PinotRuntimeException, WDYT?
| StringBuilder message = new StringBuilder("Error when processing"); | ||
| if (_tableName != null) { | ||
| message.append(" ").append(_tableName); | ||
| } | ||
| if (_columnName != null) { | ||
| if (_tableName != null) { | ||
| message.append("."); | ||
| } else { | ||
| message.append(" "); | ||
| } | ||
| message.append(_columnName); | ||
| } | ||
| message.append(": "); | ||
| message.append(super.getMessage()); | ||
|
|
||
| return message.toString(); |
There was a problem hiding this comment.
Might be clearer to change this to something like Error when processing column "abc" in table "xyz"?
There was a problem hiding this comment.
I'm open to suggestions. We've to keep in mind that table could actually mean alias and column - an expression - e.g. max(x) so it should be generic but still readable :).
I guess we can add more useful bits of context data.
| recordReadValues(scope, DataType.FLOAT, true); | ||
| return _dataBlockCache.getFloatValuesForSVColumn(_column); | ||
| } catch (RuntimeException re) { | ||
| //add column information, then later enrich it with table name |
There was a problem hiding this comment.
then later enrich it with table name
I presume this means we don't have access to the table name information in this class currently?
There was a problem hiding this comment.
I think table name was missing when I debugged some cases.
Let me double check .
| try (InvocationScope scope = Tracing.getTracer().createScope(ProjectionBlockValSet.class)) { | ||
| recordReadValues(scope, DataType.DOUBLE, true); | ||
| return _dataBlockCache.getDoubleValuesForSVColumn(_column); | ||
| } catch (RuntimeException re) { |
There was a problem hiding this comment.
Why is the error handling only added for float and double here?
There was a problem hiding this comment.
Probably because it's really hard to trigger those conversions in both engines :)
You're right, I'll add them and try to test with stub aggregate.
| return ((Number) value).doubleValue(); | ||
| } else { | ||
| return Double.parseDouble(value.toString()); | ||
| private static Double toDouble(Comparable<?> value, DataSource dataSource) { |
There was a problem hiding this comment.
We could just pass in the column name directly here I suppose?
There was a problem hiding this comment.
I guess so but I'm not sure how sensitive the place and tried to minimize potential overhead.
| putValues(FieldSpec.DataType.INT, column, intValues); | ||
| } | ||
| _dataFetcher.fetchIntValues(column, _docIds, _length, intValues); | ||
| try { |
There was a problem hiding this comment.
This is a nit: Have you considered adding the catch block in
instead since there is a try block already ?| // NOTE: We need to handle Error here, or the execution threads will die without adding the execution | ||
| // exception into the query response, and the main thread might wait infinitely (until timeout) or | ||
| // throw unexpected exceptions (such as NPE). | ||
| PinotRuntimeException pe = PinotRuntimeException.create(t).withTableName(_queryContext.getTableName()); |
There was a problem hiding this comment.
One issue is that other types of exceptions like NPE may get missed when looking at stack traces. I know that the NPE will still appear in the stack trace etc. However as on-call our senses are trained to certain exception types in the top message. Is there an example of how the error looks like ? Maybe you can simulate by forcibly throwing an NPE ?
| values[matchedRowId] = ((Number) _rows.get(rowId)[_colId]).longValue(); | ||
| } else { | ||
| values[matchedRowId] = Long.parseLong((String) _rows.get(rowId)[_colId]); | ||
| try { |
There was a problem hiding this comment.
I cant comment at the right line no. In line 136, column names maybe useful in Precondition checks as well. I dont know how often they are triggered though.
gortiz
left a comment
There was a problem hiding this comment.
The name PinotRuntimeException is too generic and therefore cannot be used to identify an error associated with a column during query execution. We need to think about other areas of the code like ingeston or system management that can also fail with runtime exceptions.
I'm not asking to create the whole exception hierarchy I suggested in https://github.com/apache/pinot/pull/14388/files#r1829241167, but at least we need to rename the exception to something like ColumnExecutionQueryException (or some acronym like ColExecQException).
At the moment exceptions thrown during query execution are often missing basic table and column information, making it hard to diagnose issues.
For example, if dat is a string column containing dates then issuing following query :
might return
NumberFormatException: For input string: "2021-10-01T09:01:00-0800".This PR adds table/alias and column/expression information to conversion errors, e.g.
Error when processing table.dat: NumberFormatException: For input string: "2021-10-01T09:01:00-0800".