diff --git a/web-console/src/components/json-input/json-input.tsx b/web-console/src/components/json-input/json-input.tsx index e3467303e36..b206db77e99 100644 --- a/web-console/src/components/json-input/json-input.tsx +++ b/web-console/src/components/json-input/json-input.tsx @@ -67,7 +67,7 @@ interface InternalValue { interface JsonInputProps { value: any; onChange: (value: any) => void; - onError?: (error: Error) => void; + setError?: (error: Error | undefined) => void; placeholder?: string; focus?: boolean; width?: string; @@ -76,7 +76,7 @@ interface JsonInputProps { } export const JsonInput = React.memo(function JsonInput(props: JsonInputProps) { - const { onChange, onError, placeholder, focus, width, height, value, issueWithValue } = props; + const { onChange, setError, placeholder, focus, width, height, value, issueWithValue } = props; const [internalValue, setInternalValue] = useState(() => ({ value, stringified: stringifyJson(value), @@ -121,9 +121,8 @@ export const JsonInput = React.memo(function JsonInput(props: JsonInputProps) { stringified: inputJson, }); - if (error) { - onError?.(error); - } else { + setError?.(error); + if (!error) { onChange(value); } diff --git a/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.tsx b/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.tsx index 7c70d22571a..8a3a232072e 100644 --- a/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.tsx +++ b/web-console/src/dialogs/compaction-config-dialog/compaction-config-dialog.tsx @@ -87,7 +87,13 @@ export const CompactionConfigDialog = React.memo(function CompactionConfigDialog

)} - + { + setJsonError(undefined); + setCurrentTab(t); + }} + />
{currentTab === 'form' ? ( { - setCurrentConfig(v); - setJsonError(undefined); - }} - onError={setJsonError} + onChange={setCurrentConfig} + setError={setJsonError} issueWithValue={value => AutoForm.issueWithModel(value, COMPACTION_CONFIG_FIELDS)} height="100%" /> diff --git a/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.tsx b/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.tsx index 118f53aef54..b31f774d88b 100644 --- a/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.tsx +++ b/web-console/src/dialogs/coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog.tsx @@ -114,7 +114,13 @@ export const CoordinatorDynamicConfigDialog = React.memo(function CoordinatorDyn .

- + { + setJsonError(undefined); + setCurrentTab(t); + }} + /> {currentTab === 'form' ? ( { - setDynamicConfig(v); - setJsonError(undefined); - }} - onError={setJsonError} + onChange={setDynamicConfig} + setError={setJsonError} /> )} diff --git a/web-console/src/dialogs/index-spec-dialog/index-spec-dialog.tsx b/web-console/src/dialogs/index-spec-dialog/index-spec-dialog.tsx index daa9e060699..5f579953d1b 100644 --- a/web-console/src/dialogs/index-spec-dialog/index-spec-dialog.tsx +++ b/web-console/src/dialogs/index-spec-dialog/index-spec-dialog.tsx @@ -50,22 +50,25 @@ export const IndexSpecDialog = React.memo(function IndexSpecDialog(props: IndexS canOutsideClickClose={false} title={title ?? 'Index spec'} > - + { + setJsonError(undefined); + setCurrentTab(t); + }} + />
{currentTab === 'form' ? ( setCurrentIndexSpec(m)} + onChange={setCurrentIndexSpec} /> ) : ( { - setCurrentIndexSpec(v); - setJsonError(undefined); - }} - onError={setJsonError} + onChange={setCurrentIndexSpec} + setError={setJsonError} issueWithValue={value => AutoForm.issueWithModel(value, INDEX_SPEC_FIELDS)} height="100%" /> diff --git a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx index 0903ede6f96..6109dfb9219 100644 --- a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx +++ b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx @@ -121,7 +121,13 @@ export const LookupEditDialog = React.memo(function LookupEditDialog(props: Look } /> - + { + setJsonError(undefined); + setCurrentTab(t); + }} + /> {currentTab === 'form' ? ( { - onChange('spec', m); - setJsonError(undefined); - }} - onError={setJsonError} + onChange={m => onChange('spec', m)} + setError={setJsonError} issueWithValue={spec => AutoForm.issueWithModel(spec, LOOKUP_FIELDS)} /> )} diff --git a/web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.tsx b/web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.tsx index fa31d2378b2..109901a71b2 100644 --- a/web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.tsx +++ b/web-console/src/dialogs/overlord-dynamic-config-dialog/overlord-dynamic-config-dialog.tsx @@ -114,7 +114,13 @@ export const OverlordDynamicConfigDialog = React.memo(function OverlordDynamicCo .

- + { + setJsonError(undefined); + setCurrentTab(t); + }} + /> {currentTab === 'form' ? ( { - setDynamicConfig(v); - setJsonError(undefined); - }} - onError={setJsonError} + onChange={setDynamicConfig} + setError={setJsonError} /> )} diff --git a/web-console/src/dialogs/retention-dialog/retention-dialog.tsx b/web-console/src/dialogs/retention-dialog/retention-dialog.tsx index 547fd99ee3c..64204137067 100644 --- a/web-console/src/dialogs/retention-dialog/retention-dialog.tsx +++ b/web-console/src/dialogs/retention-dialog/retention-dialog.tsx @@ -187,7 +187,7 @@ ORDER BY 1`, )} diff --git a/web-console/src/druid-models/async-query/async-query.ts b/web-console/src/druid-models/async-query/async-query.ts index 401569f558b..e823ba6a17e 100644 --- a/web-console/src/druid-models/async-query/async-query.ts +++ b/web-console/src/druid-models/async-query/async-query.ts @@ -28,10 +28,14 @@ export interface AsyncStatusResponse { schema?: { name: string; type: string; nativeType: string }[]; result?: { dataSource: string; - sampleRecords: any[][]; + sampleRecords?: any[][]; numTotalRows: number; totalSizeInBytes: number; - pages: any[]; + pages?: { + id: number; + numRows: number; + sizeInBytes: number; + }[]; }; errorDetails?: ErrorResponse; } diff --git a/web-console/src/druid-models/execution/execution-ingest-complete.mock.ts b/web-console/src/druid-models/execution/execution-ingest-complete.mock.ts index 797fdd5f25c..3ae77b27ef2 100644 --- a/web-console/src/druid-models/execution/execution-ingest-complete.mock.ts +++ b/web-console/src/druid-models/execution/execution-ingest-complete.mock.ts @@ -43,12 +43,12 @@ PARTITIONED BY ALL TIME export const EXECUTION_INGEST_COMPLETE = Execution.fromTaskReport({ multiStageQuery: { type: 'multiStageQuery', - taskId: 'query-5aa683e2-a6ee-4655-a834-a643e91055b1', + taskId: 'query-93a855fa-c35a-48df-b596-6bc98eed1101', payload: { status: { status: 'SUCCESS', - startTime: '2023-06-19T05:39:26.377Z', - durationMs: 23170, + startTime: '2023-08-01T03:12:59.527Z', + durationMs: 23699, pendingTasks: 0, runningTasks: 2, }, @@ -56,7 +56,7 @@ export const EXECUTION_INGEST_COMPLETE = Execution.fromTaskReport({ { stageNumber: 0, definition: { - id: '8af42220-2724-4a76-b39f-c2f98df2de69_0', + id: 'ad318360-2ccf-4afc-b221-27c8704bf4fe_0', input: [ { type: 'external', @@ -129,16 +129,17 @@ export const EXECUTION_INGEST_COMPLETE = Execution.fromTaskReport({ context: { __timeColumn: 'v0', __user: 'allowAll', + executionMode: 'async', finalize: false, finalizeAggregations: false, groupByEnableMultiValueUnnesting: false, maxNumTasks: 2, maxParseExceptions: 0, - queryId: '5aa683e2-a6ee-4655-a834-a643e91055b1', + queryId: '93a855fa-c35a-48df-b596-6bc98eed1101', scanSignature: '[{"name":"agent_type","type":"STRING"},{"name":"v0","type":"LONG"}]', sqlInsertSegmentGranularity: '{"type":"all"}', - sqlQueryId: '5aa683e2-a6ee-4655-a834-a643e91055b1', + sqlQueryId: '93a855fa-c35a-48df-b596-6bc98eed1101', sqlReplaceTimeChunks: 'all', }, granularity: { @@ -178,14 +179,14 @@ export const EXECUTION_INGEST_COMPLETE = Execution.fromTaskReport({ phase: 'FINISHED', workerCount: 1, partitionCount: 1, - startTime: '2023-06-19T05:39:26.711Z', - duration: 20483, + startTime: '2023-08-01T03:12:59.865Z', + duration: 21324, sort: true, }, { stageNumber: 1, definition: { - id: '8af42220-2724-4a76-b39f-c2f98df2de69_1', + id: 'ad318360-2ccf-4afc-b221-27c8704bf4fe_1', input: [ { type: 'stage', @@ -250,8 +251,8 @@ export const EXECUTION_INGEST_COMPLETE = Execution.fromTaskReport({ phase: 'FINISHED', workerCount: 1, partitionCount: 1, - startTime: '2023-06-19T05:39:47.166Z', - duration: 2381, + startTime: '2023-08-01T03:13:21.156Z', + duration: 2070, }, ], counters: { @@ -314,120 +315,135 @@ export const EXECUTION_INGEST_COMPLETE = Execution.fromTaskReport({ }, }, }, -}).updateWithTaskPayload({ - task: 'query-5aa683e2-a6ee-4655-a834-a643e91055b1', - payload: { - type: 'query_controller', - id: 'query-5aa683e2-a6ee-4655-a834-a643e91055b1', - spec: { - query: { - queryType: 'scan', - dataSource: { - type: 'external', - inputSource: { - type: 'http', - uris: ['https://static.imply.io/example-data/kttm-v2/kttm-v2-2019-08-25.json.gz'], - }, - inputFormat: { - type: 'json', - keepNullColumns: false, - assumeNewlineDelimited: false, - useJsonNodeReader: false, - }, - signature: [ - { - name: 'timestamp', - type: 'STRING', +}) + .updateWithTaskPayload({ + task: 'query-93a855fa-c35a-48df-b596-6bc98eed1101', + payload: { + type: 'query_controller', + id: 'query-93a855fa-c35a-48df-b596-6bc98eed1101', + spec: { + query: { + queryType: 'scan', + dataSource: { + type: 'external', + inputSource: { + type: 'http', + uris: ['https://static.imply.io/example-data/kttm-v2/kttm-v2-2019-08-25.json.gz'], }, + inputFormat: { + type: 'json', + keepNullColumns: false, + assumeNewlineDelimited: false, + useJsonNodeReader: false, + }, + signature: [ + { + name: 'timestamp', + type: 'STRING', + }, + { + name: 'agent_type', + type: 'STRING', + }, + ], + }, + intervals: { + type: 'intervals', + intervals: ['-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z'], + }, + virtualColumns: [ { - name: 'agent_type', - type: 'STRING', + type: 'expression', + name: 'v0', + expression: 'timestamp_parse("timestamp",null,\'UTC\')', + outputType: 'LONG', }, ], + resultFormat: 'compactedList', + columns: ['agent_type', 'v0'], + legacy: false, + context: { + __user: 'allowAll', + executionMode: 'async', + finalize: false, + finalizeAggregations: false, + groupByEnableMultiValueUnnesting: false, + maxNumTasks: 2, + maxParseExceptions: 0, + queryId: '93a855fa-c35a-48df-b596-6bc98eed1101', + scanSignature: '[{"name":"agent_type","type":"STRING"},{"name":"v0","type":"LONG"}]', + sqlInsertSegmentGranularity: '{"type":"all"}', + sqlQueryId: '93a855fa-c35a-48df-b596-6bc98eed1101', + sqlReplaceTimeChunks: 'all', + }, + granularity: { + type: 'all', + }, }, - intervals: { - type: 'intervals', - intervals: ['-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z'], - }, - virtualColumns: [ + columnMappings: [ { - type: 'expression', - name: 'v0', - expression: 'timestamp_parse("timestamp",null,\'UTC\')', - outputType: 'LONG', + queryColumn: 'v0', + outputColumn: '__time', + }, + { + queryColumn: 'agent_type', + outputColumn: 'agent_type', }, ], - resultFormat: 'compactedList', - columns: ['agent_type', 'v0'], - legacy: false, - context: { - __user: 'allowAll', - finalize: false, - finalizeAggregations: false, - groupByEnableMultiValueUnnesting: false, - maxNumTasks: 2, - maxParseExceptions: 0, - queryId: '5aa683e2-a6ee-4655-a834-a643e91055b1', - scanSignature: '[{"name":"agent_type","type":"STRING"},{"name":"v0","type":"LONG"}]', - sqlInsertSegmentGranularity: '{"type":"all"}', - sqlQueryId: '5aa683e2-a6ee-4655-a834-a643e91055b1', - sqlReplaceTimeChunks: 'all', + destination: { + type: 'dataSource', + dataSource: 'kttm_simple', + segmentGranularity: { + type: 'all', + }, + replaceTimeChunks: ['-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z'], }, - granularity: { - type: 'all', + assignmentStrategy: 'max', + tuningConfig: { + maxNumWorkers: 1, + maxRowsInMemory: 100000, + rowsPerSegment: 3000000, }, }, - columnMappings: [ - { - queryColumn: 'v0', - outputColumn: '__time', - }, - { - queryColumn: 'agent_type', - outputColumn: 'agent_type', - }, - ], - destination: { - type: 'dataSource', - dataSource: 'kttm_simple', - segmentGranularity: { - type: 'all', - }, - replaceTimeChunks: ['-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z'], + sqlQuery: + 'REPLACE INTO "kttm_simple" OVERWRITE ALL\nSELECT\n TIME_PARSE("timestamp") AS "__time",\n "agent_type"\nFROM TABLE(\n EXTERN(\n \'{"type":"http","uris":["https://static.imply.io/example-data/kttm-v2/kttm-v2-2019-08-25.json.gz"]}\',\n \'{"type":"json"}\'\n )\n) EXTEND ("timestamp" VARCHAR, "agent_type" VARCHAR)\nPARTITIONED BY ALL TIME', + sqlQueryContext: { + finalizeAggregations: false, + sqlQueryId: '93a855fa-c35a-48df-b596-6bc98eed1101', + groupByEnableMultiValueUnnesting: false, + sqlInsertSegmentGranularity: '{"type":"all"}', + maxNumTasks: 2, + sqlReplaceTimeChunks: 'all', + executionMode: 'async', + queryId: '93a855fa-c35a-48df-b596-6bc98eed1101', }, - assignmentStrategy: 'max', - tuningConfig: { - maxNumWorkers: 1, - maxRowsInMemory: 100000, - rowsPerSegment: 3000000, + sqlResultsContext: { + timeZone: 'UTC', + serializeComplexValues: true, + stringifyArrays: true, + }, + sqlTypeNames: ['TIMESTAMP', 'VARCHAR'], + nativeTypeNames: ['LONG', 'STRING'], + context: { + forceTimeChunkLock: true, + useLineageBasedSegmentAllocation: true, + }, + groupId: 'query-93a855fa-c35a-48df-b596-6bc98eed1101', + dataSource: 'kttm_simple', + resource: { + availabilityGroup: 'query-93a855fa-c35a-48df-b596-6bc98eed1101', + requiredCapacity: 1, }, }, - sqlQuery: - 'REPLACE INTO "kttm_simple" OVERWRITE ALL\nSELECT\n TIME_PARSE("timestamp") AS "__time",\n "agent_type"\nFROM TABLE(\n EXTERN(\n \'{"type":"http","uris":["https://static.imply.io/example-data/kttm-v2/kttm-v2-2019-08-25.json.gz"]}\',\n \'{"type":"json"}\'\n )\n) EXTEND ("timestamp" VARCHAR, "agent_type" VARCHAR)\nPARTITIONED BY ALL TIME', - sqlQueryContext: { - finalizeAggregations: false, - sqlQueryId: '5aa683e2-a6ee-4655-a834-a643e91055b1', - groupByEnableMultiValueUnnesting: false, - sqlInsertSegmentGranularity: '{"type":"all"}', - maxNumTasks: 2, - sqlReplaceTimeChunks: 'all', - queryId: '5aa683e2-a6ee-4655-a834-a643e91055b1', + }) + .updateWithAsyncStatus({ + queryId: 'query-93a855fa-c35a-48df-b596-6bc98eed1101', + state: 'SUCCESS', + createdAt: '2023-08-01T03:12:50.121Z', + durationMs: 37657, + result: { + numTotalRows: 465346, + totalSizeInBytes: 0, + dataSource: 'kttm_simple', }, - sqlResultsContext: { - timeZone: 'UTC', - serializeComplexValues: true, - stringifyArrays: true, - }, - sqlTypeNames: ['TIMESTAMP', 'VARCHAR'], - context: { - forceTimeChunkLock: true, - useLineageBasedSegmentAllocation: true, - }, - groupId: 'query-5aa683e2-a6ee-4655-a834-a643e91055b1', - dataSource: 'kttm_simple', - resource: { - availabilityGroup: 'query-5aa683e2-a6ee-4655-a834-a643e91055b1', - requiredCapacity: 1, - }, - }, -}); + }); diff --git a/web-console/src/druid-models/execution/execution-ingest-error.mock.ts b/web-console/src/druid-models/execution/execution-ingest-error.mock.ts index 15715a1a0e0..9d58a7e558c 100644 --- a/web-console/src/druid-models/execution/execution-ingest-error.mock.ts +++ b/web-console/src/druid-models/execution/execution-ingest-error.mock.ts @@ -44,12 +44,12 @@ PARTITIONED BY DAY export const EXECUTION_INGEST_ERROR = Execution.fromTaskReport({ multiStageQuery: { type: 'multiStageQuery', - taskId: 'query-8f889312-e989-4b4c-9895-485a1fe796d3', + taskId: 'query-af8a263d-213f-418e-ad8d-37d55beff59b', payload: { status: { status: 'FAILED', errorReport: { - taskId: 'query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0', + taskId: 'query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0', host: 'localhost', error: { errorCode: 'TooManyWarnings', @@ -60,7 +60,7 @@ export const EXECUTION_INGEST_ERROR = Execution.fromTaskReport({ }, warnings: [ { - taskId: 'query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0', + taskId: 'query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0', host: 'localhost:8101', stageNumber: 0, error: { @@ -69,10 +69,10 @@ export const EXECUTION_INGEST_ERROR = Execution.fromTaskReport({ 'Unable to parse row [] (Path: https://static.imply.io/example-data/kttm-with-issues/kttm-blank-lines.json, Record: 3, Line: 3)', }, exceptionStackTrace: - 'org.apache.druid.java.util.common.parsers.ParseException: Unable to parse row [] (Path: https://static.imply.io/example-data/kttm-with-issues/kttm-blank-lines.json, Record: 3, Line: 3)\n\tat org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:79)\n\tat org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)\n\tat org.apache.druid.java.util.common.parsers.CloseableIterator$2.next(CloseableIterator.java:108)\n\tat org.apache.druid.java.util.common.parsers.CloseableIterator$1.next(CloseableIterator.java:52)\n\tat org.apache.druid.msq.input.external.ExternalInputSliceReader$1$1.hasNext(ExternalInputSliceReader.java:183)\n\tat org.apache.druid.java.util.common.guava.BaseSequence$1.next(BaseSequence.java:115)\n\tat org.apache.druid.segment.RowWalker.advance(RowWalker.java:70)\n\tat org.apache.druid.segment.RowBasedCursor.advanceUninterruptibly(RowBasedCursor.java:110)\n\tat org.apache.druid.segment.RowBasedCursor.advance(RowBasedCursor.java:103)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:246)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:173)\n\tat org.apache.druid.msq.querykit.BaseLeafFrameProcessor.runIncrementally(BaseLeafFrameProcessor.java:159)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:138)\n\tat org.apache.druid.frame.processor.FrameProcessors$1FrameProcessorWithBaggage.runIncrementally(FrameProcessors.java:75)\n\tat org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.runProcessorNow(FrameProcessorExecutor.java:229)\n\tat org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.run(FrameProcessorExecutor.java:137)\n\tat org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:820)\n\tat java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)\n\tat org.apache.druid.query.PrioritizedListenableFutureTask.run(PrioritizedExecutorService.java:251)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat java.base/java.lang.Thread.run(Thread.java:829)\nCaused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input\n at [Source: (String)""; line: 1, column: 0]\n\tat com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)\n\tat com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4360)\n\tat com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4205)\n\tat com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3214)\n\tat com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3182)\n\tat org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:75)\n\tat org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:48)\n\tat org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:71)\n\t... 22 more\n', + 'org.apache.druid.java.util.common.parsers.ParseException: Unable to parse row [] (Path: https://static.imply.io/example-data/kttm-with-issues/kttm-blank-lines.json, Record: 3, Line: 3)\n\tat org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:79)\n\tat org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)\n\tat org.apache.druid.java.util.common.parsers.CloseableIterator$2.next(CloseableIterator.java:108)\n\tat org.apache.druid.java.util.common.parsers.CloseableIterator$1.next(CloseableIterator.java:52)\n\tat org.apache.druid.msq.input.external.ExternalSegment$1$1.hasNext(ExternalSegment.java:95)\n\tat org.apache.druid.java.util.common.guava.BaseSequence$1.next(BaseSequence.java:115)\n\tat org.apache.druid.segment.RowWalker.advance(RowWalker.java:70)\n\tat org.apache.druid.segment.RowBasedCursor.advanceUninterruptibly(RowBasedCursor.java:110)\n\tat org.apache.druid.segment.RowBasedCursor.advance(RowBasedCursor.java:103)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:275)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeededWithExceptionHandling(ScanQueryFrameProcessor.java:242)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:181)\n\tat org.apache.druid.msq.querykit.BaseLeafFrameProcessor.runIncrementally(BaseLeafFrameProcessor.java:159)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:146)\n\tat org.apache.druid.frame.processor.FrameProcessors$1FrameProcessorWithBaggage.runIncrementally(FrameProcessors.java:75)\n\tat org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.runProcessorNow(FrameProcessorExecutor.java:229)\n\tat org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.run(FrameProcessorExecutor.java:137)\n\tat org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:837)\n\tat java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)\n\tat org.apache.druid.query.PrioritizedListenableFutureTask.run(PrioritizedExecutorService.java:251)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat java.base/java.lang.Thread.run(Thread.java:829)\nCaused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input\n at [Source: (String)""; line: 1, column: 0]\n\tat com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)\n\tat com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4360)\n\tat com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4205)\n\tat com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3214)\n\tat com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3182)\n\tat org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:75)\n\tat org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:48)\n\tat org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:71)\n\t... 23 more\n', }, { - taskId: 'query-8f889312-e989-4b4c-9895-485a1fe796d3-worker0_0', + taskId: 'query-af8a263d-213f-418e-ad8d-37d55beff59b-worker0_0', host: 'localhost:8101', stageNumber: 0, error: { @@ -81,11 +81,11 @@ export const EXECUTION_INGEST_ERROR = Execution.fromTaskReport({ 'Unable to parse row [] (Path: https://static.imply.io/example-data/kttm-with-issues/kttm-blank-lines.json, Record: 6, Line: 7)', }, exceptionStackTrace: - 'org.apache.druid.java.util.common.parsers.ParseException: Unable to parse row [] (Path: https://static.imply.io/example-data/kttm-with-issues/kttm-blank-lines.json, Record: 6, Line: 7)\n\tat org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:79)\n\tat org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)\n\tat org.apache.druid.java.util.common.parsers.CloseableIterator$2.next(CloseableIterator.java:108)\n\tat org.apache.druid.java.util.common.parsers.CloseableIterator$1.next(CloseableIterator.java:52)\n\tat org.apache.druid.msq.input.external.ExternalInputSliceReader$1$1.hasNext(ExternalInputSliceReader.java:183)\n\tat org.apache.druid.java.util.common.guava.BaseSequence$1.next(BaseSequence.java:115)\n\tat org.apache.druid.segment.RowWalker.advance(RowWalker.java:70)\n\tat org.apache.druid.segment.RowBasedCursor.advanceUninterruptibly(RowBasedCursor.java:110)\n\tat org.apache.druid.segment.RowBasedCursor.advance(RowBasedCursor.java:103)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:246)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:173)\n\tat org.apache.druid.msq.querykit.BaseLeafFrameProcessor.runIncrementally(BaseLeafFrameProcessor.java:159)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:138)\n\tat org.apache.druid.frame.processor.FrameProcessors$1FrameProcessorWithBaggage.runIncrementally(FrameProcessors.java:75)\n\tat org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.runProcessorNow(FrameProcessorExecutor.java:229)\n\tat org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.run(FrameProcessorExecutor.java:137)\n\tat org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:820)\n\tat java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)\n\tat org.apache.druid.query.PrioritizedListenableFutureTask.run(PrioritizedExecutorService.java:251)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat java.base/java.lang.Thread.run(Thread.java:829)\nCaused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input\n at [Source: (String)""; line: 1, column: 0]\n\tat com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)\n\tat com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4360)\n\tat com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4205)\n\tat com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3214)\n\tat com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3182)\n\tat org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:75)\n\tat org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:48)\n\tat org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:71)\n\t... 22 more\n', + 'org.apache.druid.java.util.common.parsers.ParseException: Unable to parse row [] (Path: https://static.imply.io/example-data/kttm-with-issues/kttm-blank-lines.json, Record: 6, Line: 7)\n\tat org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:79)\n\tat org.apache.druid.java.util.common.parsers.CloseableIterator$2.findNextIteratorIfNecessary(CloseableIterator.java:74)\n\tat org.apache.druid.java.util.common.parsers.CloseableIterator$2.next(CloseableIterator.java:108)\n\tat org.apache.druid.java.util.common.parsers.CloseableIterator$1.next(CloseableIterator.java:52)\n\tat org.apache.druid.msq.input.external.ExternalSegment$1$1.hasNext(ExternalSegment.java:95)\n\tat org.apache.druid.java.util.common.guava.BaseSequence$1.next(BaseSequence.java:115)\n\tat org.apache.druid.segment.RowWalker.advance(RowWalker.java:70)\n\tat org.apache.druid.segment.RowBasedCursor.advanceUninterruptibly(RowBasedCursor.java:110)\n\tat org.apache.druid.segment.RowBasedCursor.advance(RowBasedCursor.java:103)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeeded(ScanQueryFrameProcessor.java:275)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.populateFrameWriterAndFlushIfNeededWithExceptionHandling(ScanQueryFrameProcessor.java:242)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runWithSegment(ScanQueryFrameProcessor.java:181)\n\tat org.apache.druid.msq.querykit.BaseLeafFrameProcessor.runIncrementally(BaseLeafFrameProcessor.java:159)\n\tat org.apache.druid.msq.querykit.scan.ScanQueryFrameProcessor.runIncrementally(ScanQueryFrameProcessor.java:146)\n\tat org.apache.druid.frame.processor.FrameProcessors$1FrameProcessorWithBaggage.runIncrementally(FrameProcessors.java:75)\n\tat org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.runProcessorNow(FrameProcessorExecutor.java:229)\n\tat org.apache.druid.frame.processor.FrameProcessorExecutor$1ExecutorRunnable.run(FrameProcessorExecutor.java:137)\n\tat org.apache.druid.msq.exec.WorkerImpl$1$2.run(WorkerImpl.java:837)\n\tat java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)\n\tat org.apache.druid.query.PrioritizedListenableFutureTask.run(PrioritizedExecutorService.java:251)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat java.base/java.lang.Thread.run(Thread.java:829)\nCaused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input\n at [Source: (String)""; line: 1, column: 0]\n\tat com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)\n\tat com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4360)\n\tat com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4205)\n\tat com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3214)\n\tat com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3182)\n\tat org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:75)\n\tat org.apache.druid.data.input.impl.JsonLineReader.parseInputRows(JsonLineReader.java:48)\n\tat org.apache.druid.data.input.IntermediateRowParsingReader$1.hasNext(IntermediateRowParsingReader.java:71)\n\t... 23 more\n', }, ], - startTime: '2023-06-19T05:37:48.605Z', - durationMs: 14760, + startTime: '2023-08-01T04:20:24.945Z', + durationMs: 14545, pendingTasks: 0, runningTasks: 2, }, @@ -93,7 +93,7 @@ export const EXECUTION_INGEST_ERROR = Execution.fromTaskReport({ { stageNumber: 0, definition: { - id: 'd337a3d8-e361-4795-8eaa-97ced72d9a7b_0', + id: 'f635e36d-6b90-4b74-ad5e-a179c99f0ddc_0', input: [ { type: 'external', @@ -168,16 +168,17 @@ export const EXECUTION_INGEST_ERROR = Execution.fromTaskReport({ context: { __timeColumn: 'v0', __user: 'allowAll', + executionMode: 'async', finalize: false, finalizeAggregations: false, groupByEnableMultiValueUnnesting: false, maxNumTasks: 2, maxParseExceptions: 2, - queryId: '8f889312-e989-4b4c-9895-485a1fe796d3', + queryId: 'af8a263d-213f-418e-ad8d-37d55beff59b', scanSignature: '[{"name":"agent_type","type":"STRING"},{"name":"v0","type":"LONG"}]', sqlInsertSegmentGranularity: '"DAY"', - sqlQueryId: '8f889312-e989-4b4c-9895-485a1fe796d3', + sqlQueryId: 'af8a263d-213f-418e-ad8d-37d55beff59b', sqlReplaceTimeChunks: 'all', }, granularity: { @@ -225,14 +226,14 @@ export const EXECUTION_INGEST_ERROR = Execution.fromTaskReport({ }, phase: 'FAILED', workerCount: 1, - startTime: '2023-06-19T05:37:48.952Z', - duration: 14412, + startTime: '2023-08-01T04:20:25.296Z', + duration: 14193, sort: true, }, { stageNumber: 1, definition: { - id: 'd337a3d8-e361-4795-8eaa-97ced72d9a7b_1', + id: 'f635e36d-6b90-4b74-ad5e-a179c99f0ddc_1', input: [ { type: 'stage', @@ -334,10 +335,10 @@ export const EXECUTION_INGEST_ERROR = Execution.fromTaskReport({ }, }, }).updateWithTaskPayload({ - task: 'query-8f889312-e989-4b4c-9895-485a1fe796d3', + task: 'query-af8a263d-213f-418e-ad8d-37d55beff59b', payload: { type: 'query_controller', - id: 'query-8f889312-e989-4b4c-9895-485a1fe796d3', + id: 'query-af8a263d-213f-418e-ad8d-37d55beff59b', spec: { query: { queryType: 'scan', @@ -381,15 +382,16 @@ export const EXECUTION_INGEST_ERROR = Execution.fromTaskReport({ legacy: false, context: { __user: 'allowAll', + executionMode: 'async', finalize: false, finalizeAggregations: false, groupByEnableMultiValueUnnesting: false, maxNumTasks: 2, maxParseExceptions: 2, - queryId: '8f889312-e989-4b4c-9895-485a1fe796d3', + queryId: 'af8a263d-213f-418e-ad8d-37d55beff59b', scanSignature: '[{"name":"agent_type","type":"STRING"},{"name":"v0","type":"LONG"}]', sqlInsertSegmentGranularity: '"DAY"', - sqlQueryId: '8f889312-e989-4b4c-9895-485a1fe796d3', + sqlQueryId: 'af8a263d-213f-418e-ad8d-37d55beff59b', sqlReplaceTimeChunks: 'all', }, granularity: { @@ -424,12 +426,13 @@ export const EXECUTION_INGEST_ERROR = Execution.fromTaskReport({ sqlQueryContext: { maxParseExceptions: 2, finalizeAggregations: false, - sqlQueryId: '8f889312-e989-4b4c-9895-485a1fe796d3', + sqlQueryId: 'af8a263d-213f-418e-ad8d-37d55beff59b', groupByEnableMultiValueUnnesting: false, sqlInsertSegmentGranularity: '"DAY"', maxNumTasks: 2, sqlReplaceTimeChunks: 'all', - queryId: '8f889312-e989-4b4c-9895-485a1fe796d3', + executionMode: 'async', + queryId: 'af8a263d-213f-418e-ad8d-37d55beff59b', }, sqlResultsContext: { timeZone: 'UTC', @@ -437,14 +440,15 @@ export const EXECUTION_INGEST_ERROR = Execution.fromTaskReport({ stringifyArrays: true, }, sqlTypeNames: ['TIMESTAMP', 'VARCHAR'], + nativeTypeNames: ['LONG', 'STRING'], context: { forceTimeChunkLock: true, useLineageBasedSegmentAllocation: true, }, - groupId: 'query-8f889312-e989-4b4c-9895-485a1fe796d3', + groupId: 'query-af8a263d-213f-418e-ad8d-37d55beff59b', dataSource: 'kttm-blank-lines', resource: { - availabilityGroup: 'query-8f889312-e989-4b4c-9895-485a1fe796d3', + availabilityGroup: 'query-af8a263d-213f-418e-ad8d-37d55beff59b', requiredCapacity: 1, }, }, diff --git a/web-console/src/druid-models/execution/execution.spec.ts b/web-console/src/druid-models/execution/execution.spec.ts index dd7833c635e..a6b55db6808 100644 --- a/web-console/src/druid-models/execution/execution.spec.ts +++ b/web-console/src/druid-models/execution/execution.spec.ts @@ -42,10 +42,14 @@ describe('Execution', () => { "useLineageBasedSegmentAllocation": true, }, "dataSource": "kttm_simple", - "groupId": "query-5aa683e2-a6ee-4655-a834-a643e91055b1", - "id": "query-5aa683e2-a6ee-4655-a834-a643e91055b1", + "groupId": "query-93a855fa-c35a-48df-b596-6bc98eed1101", + "id": "query-93a855fa-c35a-48df-b596-6bc98eed1101", + "nativeTypeNames": Array [ + "LONG", + "STRING", + ], "resource": Object { - "availabilityGroup": "query-5aa683e2-a6ee-4655-a834-a643e91055b1", + "availabilityGroup": "query-93a855fa-c35a-48df-b596-6bc98eed1101", "requiredCapacity": 1, }, "spec": Object { @@ -77,15 +81,16 @@ describe('Execution', () => { ], "context": Object { "__user": "allowAll", + "executionMode": "async", "finalize": false, "finalizeAggregations": false, "groupByEnableMultiValueUnnesting": false, "maxNumTasks": 2, "maxParseExceptions": 0, - "queryId": "5aa683e2-a6ee-4655-a834-a643e91055b1", + "queryId": "93a855fa-c35a-48df-b596-6bc98eed1101", "scanSignature": "[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]", "sqlInsertSegmentGranularity": "{\\"type\\":\\"all\\"}", - "sqlQueryId": "5aa683e2-a6ee-4655-a834-a643e91055b1", + "sqlQueryId": "93a855fa-c35a-48df-b596-6bc98eed1101", "sqlReplaceTimeChunks": "all", }, "dataSource": Object { @@ -152,12 +157,13 @@ describe('Execution', () => { ) EXTEND (\\"timestamp\\" VARCHAR, \\"agent_type\\" VARCHAR) PARTITIONED BY ALL TIME", "sqlQueryContext": Object { + "executionMode": "async", "finalizeAggregations": false, "groupByEnableMultiValueUnnesting": false, "maxNumTasks": 2, - "queryId": "5aa683e2-a6ee-4655-a834-a643e91055b1", + "queryId": "93a855fa-c35a-48df-b596-6bc98eed1101", "sqlInsertSegmentGranularity": "{\\"type\\":\\"all\\"}", - "sqlQueryId": "5aa683e2-a6ee-4655-a834-a643e91055b1", + "sqlQueryId": "93a855fa-c35a-48df-b596-6bc98eed1101", "sqlReplaceTimeChunks": "all", }, "sqlResultsContext": Object { @@ -171,11 +177,12 @@ describe('Execution', () => { ], "type": "query_controller", }, - "task": "query-5aa683e2-a6ee-4655-a834-a643e91055b1", + "task": "query-93a855fa-c35a-48df-b596-6bc98eed1101", }, "capacityInfo": undefined, "destination": Object { "dataSource": "kttm_simple", + "numTotalRows": 465346, "replaceTimeChunks": Array [ "-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z", ], @@ -184,10 +191,11 @@ describe('Execution', () => { }, "type": "dataSource", }, - "duration": 23170, + "destinationPages": undefined, + "duration": 23699, "engine": "sql-msq-task", "error": undefined, - "id": "query-5aa683e2-a6ee-4655-a834-a643e91055b1", + "id": "query-93a855fa-c35a-48df-b596-6bc98eed1101", "nativeQuery": Object { "columns": Array [ "agent_type", @@ -195,15 +203,16 @@ describe('Execution', () => { ], "context": Object { "__user": "allowAll", + "executionMode": "async", "finalize": false, "finalizeAggregations": false, "groupByEnableMultiValueUnnesting": false, "maxNumTasks": 2, "maxParseExceptions": 0, - "queryId": "5aa683e2-a6ee-4655-a834-a643e91055b1", + "queryId": "93a855fa-c35a-48df-b596-6bc98eed1101", "scanSignature": "[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]", "sqlInsertSegmentGranularity": "{\\"type\\":\\"all\\"}", - "sqlQueryId": "5aa683e2-a6ee-4655-a834-a643e91055b1", + "sqlQueryId": "93a855fa-c35a-48df-b596-6bc98eed1101", "sqlReplaceTimeChunks": "all", }, "dataSource": Object { @@ -253,6 +262,7 @@ describe('Execution', () => { ], }, "queryContext": Object { + "executionMode": "async", "finalizeAggregations": false, "groupByEnableMultiValueUnnesting": false, "maxNumTasks": 2, @@ -357,7 +367,7 @@ describe('Execution', () => { "stages": Array [ Object { "definition": Object { - "id": "8af42220-2724-4a76-b39f-c2f98df2de69_0", + "id": "ad318360-2ccf-4afc-b221-27c8704bf4fe_0", "input": Array [ Object { "inputFormat": Object { @@ -395,15 +405,16 @@ describe('Execution', () => { "context": Object { "__timeColumn": "v0", "__user": "allowAll", + "executionMode": "async", "finalize": false, "finalizeAggregations": false, "groupByEnableMultiValueUnnesting": false, "maxNumTasks": 2, "maxParseExceptions": 0, - "queryId": "5aa683e2-a6ee-4655-a834-a643e91055b1", + "queryId": "93a855fa-c35a-48df-b596-6bc98eed1101", "scanSignature": "[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]", "sqlInsertSegmentGranularity": "{\\"type\\":\\"all\\"}", - "sqlQueryId": "5aa683e2-a6ee-4655-a834-a643e91055b1", + "sqlQueryId": "93a855fa-c35a-48df-b596-6bc98eed1101", "sqlReplaceTimeChunks": "all", }, "dataSource": Object { @@ -482,17 +493,17 @@ describe('Execution', () => { }, ], }, - "duration": 20483, + "duration": 21324, "partitionCount": 1, "phase": "FINISHED", "sort": true, "stageNumber": 0, - "startTime": "2023-06-19T05:39:26.711Z", + "startTime": "2023-08-01T03:12:59.865Z", "workerCount": 1, }, Object { "definition": Object { - "id": "8af42220-2724-4a76-b39f-c2f98df2de69_1", + "id": "ad318360-2ccf-4afc-b221-27c8704bf4fe_1", "input": Array [ Object { "stage": 0, @@ -558,16 +569,16 @@ describe('Execution', () => { }, "signature": Array [], }, - "duration": 2381, + "duration": 2070, "partitionCount": 1, "phase": "FINISHED", "stageNumber": 1, - "startTime": "2023-06-19T05:39:47.166Z", + "startTime": "2023-08-01T03:13:21.156Z", "workerCount": 1, }, ], }, - "startTime": 2023-06-19T05:39:26.377Z, + "startTime": 2023-08-01T03:12:59.527Z, "status": "SUCCESS", "usageInfo": Object { "pendingTasks": 0, @@ -586,8 +597,16 @@ describe('Execution', () => { "_payload": undefined, "capacityInfo": undefined, "destination": Object { + "numTotalRows": 2, "type": "taskReport", }, + "destinationPages": Array [ + Object { + "id": 0, + "numRows": 2, + "sizeInBytes": 116, + }, + ], "duration": 29168, "engine": "sql-msq-task", "error": undefined, @@ -640,6 +659,7 @@ describe('Execution', () => { "_payload": undefined, "capacityInfo": undefined, "destination": undefined, + "destinationPages": undefined, "duration": 11217, "engine": "sql-msq-task", "error": Object { diff --git a/web-console/src/druid-models/execution/execution.ts b/web-console/src/druid-models/execution/execution.ts index b64ba19548a..8f30711b283 100644 --- a/web-console/src/druid-models/execution/execution.ts +++ b/web-console/src/druid-models/execution/execution.ts @@ -31,12 +31,7 @@ import type { DruidEngine } from '../druid-engine/druid-engine'; import { validDruidEngine } from '../druid-engine/druid-engine'; import type { QueryContext } from '../query-context/query-context'; import { Stages } from '../stages/stages'; -import type { - MsqTaskPayloadResponse, - MsqTaskReportResponse, - TaskStatus, - TaskStatusResponse, -} from '../task/task'; +import type { MsqTaskPayloadResponse, MsqTaskReportResponse, TaskStatus } from '../task/task'; const IGNORE_CONTEXT_KEYS = [ '__asyncIdentity__', @@ -70,12 +65,19 @@ export interface ExecutionError { exceptionStackTrace?: string; } -type ExecutionDestination = +export type ExecutionDestination = | { type: 'taskReport'; + numTotalRows?: number; } - | { type: 'dataSource'; dataSource: string; numRows?: number; loaded?: boolean } - | { type: 'download' }; + | { type: 'durableStorage'; numTotalRows?: number } + | { type: 'dataSource'; dataSource: string; numTotalRows?: number; loaded?: boolean }; + +export interface ExecutionDestinationPage { + id: number; + numRows: number; + sizeInBytes: number; +} export type ExecutionStatus = 'RUNNING' | 'FAILED' | 'SUCCESS'; @@ -174,6 +176,7 @@ export interface ExecutionValue { usageInfo?: UsageInfo; stages?: Stages; destination?: ExecutionDestination; + destinationPages?: ExecutionDestinationPage[]; result?: QueryResult; error?: ExecutionError; warnings?: ExecutionError[]; @@ -214,38 +217,6 @@ export class Execution { } } - static fromTaskSubmit( - taskSubmitResult: { state: any; taskId: string; error: any }, - sqlQuery?: string, - queryContext?: QueryContext, - ): Execution { - const status = Execution.normalizeTaskStatus(taskSubmitResult.state); - return new Execution({ - engine: 'sql-msq-task', - id: taskSubmitResult.taskId, - status: taskSubmitResult.error ? 'FAILED' : status, - sqlQuery, - queryContext, - error: taskSubmitResult.error - ? { - error: { - errorCode: 'AsyncError', - errorMessage: JSON.stringify(taskSubmitResult.error), - }, - } - : status === 'FAILED' - ? { - error: { - errorCode: 'UnknownError', - errorMessage: - 'Execution failed, there is no detail information, and there is no error in the status response', - }, - } - : undefined, - destination: undefined, - }); - } - static fromAsyncStatus( asyncSubmitResult: AsyncStatusResponse, sqlQuery?: string, @@ -286,49 +257,18 @@ export class Execution { ? { type: 'dataSource', dataSource: result.dataSource, - numRows: result.numTotalRows, + numTotalRows: result.numTotalRows, } : { type: 'taskReport', + numTotalRows: result.numTotalRows, } : undefined, + destinationPages: result?.pages, result: queryResult, }); } - static fromTaskStatus( - taskStatus: TaskStatusResponse, - sqlQuery?: string, - queryContext?: QueryContext, - ): Execution { - const status = Execution.normalizeTaskStatus(taskStatus.status.status); - return new Execution({ - engine: 'sql-msq-task', - id: taskStatus.task, - status: taskStatus.status.error ? 'FAILED' : status, - usageInfo: getUsageInfoFromStatusPayload(taskStatus.status), - sqlQuery, - queryContext, - error: taskStatus.status.error - ? { - error: { - errorCode: 'AsyncError', - errorMessage: JSON.stringify(taskStatus.status.error), - }, - } - : status === 'FAILED' - ? { - error: { - errorCode: 'UnknownError', - errorMessage: - 'Execution failed, there is no detail information, and there is no error in the status response', - }, - } - : undefined, - destination: undefined, - }); - } - static fromTaskReport(taskReport: MsqTaskReportResponse): Execution { // Must have status set for a valid report const id = deepGet(taskReport, 'multiStageQuery.taskId'); @@ -424,6 +364,7 @@ export class Execution { public readonly usageInfo?: UsageInfo; public readonly stages?: Stages; public readonly destination?: ExecutionDestination; + public readonly destinationPages?: ExecutionDestinationPage[]; public readonly result?: QueryResult; public readonly error?: ExecutionError; public readonly warnings?: ExecutionError[]; @@ -444,6 +385,7 @@ export class Execution { this.usageInfo = value.usageInfo; this.stages = value.stages; this.destination = value.destination; + this.destinationPages = value.destinationPages; this.result = value.result; this.error = value.error; this.warnings = nonEmptyArray(value.warnings) ? value.warnings : undefined; @@ -465,6 +407,7 @@ export class Execution { usageInfo: this.usageInfo, stages: this.stages, destination: this.destination, + destinationPages: this.destinationPages, result: this.result, error: this.error, warnings: this.warnings, @@ -494,6 +437,13 @@ export class Execution { }); } + public changeDestinationPages(destinationPages: ExecutionDestinationPage[]): Execution { + return new Execution({ + ...this.valueOf(), + destinationPages, + }); + } + public changeResult(result: QueryResult): Execution { return new Execution({ ...this.valueOf(), @@ -514,7 +464,7 @@ export class Execution { value._payload = taskPayload; value.destination = { ...value.destination, - ...(deepGet(taskPayload, 'payload.spec.destination') || {}), + ...deepGet(taskPayload, 'payload.spec.destination'), }; value.nativeQuery = deepGet(taskPayload, 'payload.spec.query'); @@ -530,18 +480,23 @@ export class Execution { return ret; } - public attachErrorFromStatus(status: any): Execution { - const errorMsg = deepGet(status, 'status.errorMsg'); + public updateWithAsyncStatus(statusPayload: AsyncStatusResponse): Execution { + const value = this.valueOf(); - return new Execution({ - ...this.valueOf(), - error: { - error: { - errorCode: 'UnknownError', - errorMessage: errorMsg, - }, - }, - }); + const { pages, numTotalRows } = statusPayload.result || {}; + + if (!value.destinationPages && pages) { + value.destinationPages = pages; + } + + if (typeof value.destination?.numTotalRows !== 'number' && typeof numTotalRows === 'number') { + value.destination = { + ...(value.destination || { type: 'taskReport' }), + numTotalRows, + }; + } + + return new Execution(value); } public markDestinationDatasourceLoaded(): Execution { @@ -588,20 +543,8 @@ export class Execution { return destination.dataSource; } - public getIngestNumRows(): number | undefined { - const { destination, stages } = this; - - if (destination?.type === 'dataSource' && typeof destination.numRows === 'number') { - return destination.numRows; - } - - const lastStage = stages?.getLastStage(); - if (stages && lastStage && lastStage.definition.processor.type === 'segmentGenerator') { - // Assume input0 since we know the segmentGenerator will only ever have one stage input - return stages.getTotalCounterForStage(lastStage, 'input0', 'rows'); - } - - return; + public getOutputNumTotalRows(): number | undefined { + return this.destination?.numTotalRows; } public isSuccessfulInsert(): boolean { diff --git a/web-console/src/druid-models/task/task.ts b/web-console/src/druid-models/task/task.ts index 65dbb62c065..8f6f456ccb4 100644 --- a/web-console/src/druid-models/task/task.ts +++ b/web-console/src/druid-models/task/task.ts @@ -66,6 +66,7 @@ export interface MsqTaskPayloadResponse { sqlQueryContext: Record; sqlResultsContext: Record; sqlTypeNames: string[]; + nativeTypeNames: string[]; context: Record; groupId: string; dataSource: string; diff --git a/web-console/src/helpers/execution/sql-task-execution.ts b/web-console/src/helpers/execution/sql-task-execution.ts index 79e28e486d3..1643e8a076d 100644 --- a/web-console/src/helpers/execution/sql-task-execution.ts +++ b/web-console/src/helpers/execution/sql-task-execution.ts @@ -19,7 +19,7 @@ import { L, QueryResult } from '@druid-toolkit/query'; import type { AxiosResponse, CancelToken } from 'axios'; -import type { AsyncStatusResponse, QueryContext } from '../../druid-models'; +import type { AsyncStatusResponse, MsqTaskPayloadResponse, QueryContext } from '../../druid-models'; import { Execution } from '../../druid-models'; import { Api } from '../../singletons'; import { @@ -48,7 +48,6 @@ function ensureExecutionModeIsSet(context: QueryContext | undefined): QueryConte export interface SubmitTaskQueryOptions { query: string | Record; context?: QueryContext; - skipResults?: boolean; prefixLines?: number; cancelToken?: CancelToken; preserveOnTermination?: boolean; @@ -58,15 +57,7 @@ export interface SubmitTaskQueryOptions { export async function submitTaskQuery( options: SubmitTaskQueryOptions, ): Promise> { - const { - query, - context, - skipResults, - prefixLines, - cancelToken, - preserveOnTermination, - onSubmitted, - } = options; + const { query, context, prefixLines, cancelToken, preserveOnTermination, onSubmitted } = options; let sqlQuery: string; let jsonQuery: Record; @@ -123,10 +114,6 @@ export async function submitTaskQuery( onSubmitted(execution.id); } - if (skipResults) { - execution = execution.changeDestination({ type: 'download' }); - } - execution = await updateExecutionWithDatasourceLoadedIfNeeded(execution, cancelToken); if (execution.isFullyComplete()) return execution; @@ -178,7 +165,7 @@ export async function updateExecutionWithTaskIfNeeded( export async function getTaskExecution( id: string, - taskPayloadOverride?: { payload: any; task: string }, + taskPayloadOverride?: MsqTaskPayloadResponse, cancelToken?: CancelToken, ): Promise { const encodedId = Api.encodePath(id); @@ -221,7 +208,7 @@ export async function getTaskExecution( execution = Execution.fromAsyncStatus(statusResp.data); } - let taskPayload: any = taskPayloadOverride; + let taskPayload = taskPayloadOverride; if (USE_TASK_PAYLOAD && !taskPayload) { try { taskPayload = ( @@ -237,6 +224,18 @@ export async function getTaskExecution( execution = execution.updateWithTaskPayload(taskPayload); } + // Still have to pull the destination page info from the async status + if (execution.status === 'SUCCESS' && !execution.destinationPages) { + const statusResp = await Api.instance.get( + `/druid/v2/sql/statements/${encodedId}`, + { + cancelToken, + }, + ); + + execution = execution.updateWithAsyncStatus(statusResp.data); + } + if (execution.hasPotentiallyStuckStage()) { const capacityInfo = await maybeGetClusterCapacity(); if (capacityInfo) { diff --git a/web-console/src/utils/download-query-detail-archive.ts b/web-console/src/utils/download-query-detail-archive.ts index cb4dcb818b6..124b322c5fb 100644 --- a/web-console/src/utils/download-query-detail-archive.ts +++ b/web-console/src/utils/download-query-detail-archive.ts @@ -22,35 +22,39 @@ import { Api } from '../singletons'; import { downloadFile } from './download'; -interface QueryDetailArchive { +export interface QueryDetailArchive { id: string; detailArchiveVersion: number; status?: any; reports?: any; payload?: any; + statementsStatus?: any; serverStatus?: any; } export async function downloadQueryDetailArchive(id: string) { + const encodedId = Api.encodePath(id); const profile: QueryDetailArchive = { id, detailArchiveVersion: 2, }; try { - profile.status = ( - await Api.instance.get(`/druid/indexer/v1/task/${Api.encodePath(id)}/status`) - ).data; + profile.status = (await Api.instance.get(`/druid/indexer/v1/task/${encodedId}/status`)).data; } catch {} try { - profile.reports = ( - await Api.instance.get(`/druid/indexer/v1/task/${Api.encodePath(id)}/reports`) - ).data; + profile.reports = (await Api.instance.get(`/druid/indexer/v1/task/${encodedId}/reports`)).data; } catch {} try { - profile.payload = (await Api.instance.get(`/druid/indexer/v1/task/${Api.encodePath(id)}`)).data; + profile.payload = (await Api.instance.get(`/druid/indexer/v1/task/${encodedId}`)).data; + } catch {} + + try { + profile.statementsStatus = ( + await Api.instance.get(`/druid/v2/sql/statements/${encodedId}`) + ).data; } catch {} try { diff --git a/web-console/src/utils/download.ts b/web-console/src/utils/download.ts index 03705ee3b85..41c558edc02 100644 --- a/web-console/src/utils/download.ts +++ b/web-console/src/utils/download.ts @@ -22,6 +22,23 @@ import * as JSONBig from 'json-bigint-native'; import { stringifyValue } from './general'; +export function downloadUrl(url: string, filename: string) { + // Create a link and set the URL using `createObjectURL` + const link = document.createElement('a'); + link.style.display = 'none'; + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + + // To make this work on Firefox we need to wait + // a little while before removing it. + setTimeout(() => { + if (!link.parentNode) return; + link.parentNode.removeChild(link); + }, 0); +} + export function formatForFormat(s: null | string | number | Date, format: 'csv' | 'tsv'): string { // stringify and remove line break const str = stringifyValue(s).replace(/(?:\r\n|\r|\n)/g, ' '); diff --git a/web-console/src/utils/general.tsx b/web-console/src/utils/general.tsx index e9b8e8f2c1a..4d821ae0539 100644 --- a/web-console/src/utils/general.tsx +++ b/web-console/src/utils/general.tsx @@ -302,8 +302,21 @@ export function formatDurationHybrid(ms: NumberLike): string { } } +function pluralize(word: string): string { + // Ignoring irregular plurals. + if (/(s|x|z|ch|sh)$/.test(word)) { + return word + 'es'; + } else if (/([^aeiou])y$/.test(word)) { + return word.slice(0, -1) + 'ies'; + } else if (/(f|fe)$/.test(word)) { + return word.replace(/fe?$/, 'ves'); + } else { + return word + 's'; + } +} + export function pluralIfNeeded(n: NumberLike, singular: string, plural?: string): string { - if (!plural) plural = singular + 's'; + if (!plural) plural = pluralize(singular); return `${formatInteger(n)} ${n === 1 ? singular : plural}`; } diff --git a/web-console/src/views/workbench-view/destination-pages-dialog/destination-pages-dialog.scss b/web-console/src/views/workbench-view/destination-pages-dialog/destination-pages-dialog.scss new file mode 100644 index 00000000000..430cdc300d1 --- /dev/null +++ b/web-console/src/views/workbench-view/destination-pages-dialog/destination-pages-dialog.scss @@ -0,0 +1,26 @@ +/* + * 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. + */ + +@import '../../../variables'; + +.destination-pages-dialog { + &.#{$bp-ns}-dialog { + min-width: 700px; + min-height: 480px; + } +} diff --git a/web-console/src/views/workbench-view/destination-pages-dialog/destination-pages-dialog.tsx b/web-console/src/views/workbench-view/destination-pages-dialog/destination-pages-dialog.tsx new file mode 100644 index 00000000000..1e40aefdd08 --- /dev/null +++ b/web-console/src/views/workbench-view/destination-pages-dialog/destination-pages-dialog.tsx @@ -0,0 +1,49 @@ +/* + * 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. + */ + +import { Button, Classes, Dialog } from '@blueprintjs/core'; +import React from 'react'; + +import type { Execution } from '../../../druid-models'; +import { DestinationPagesPane } from '../destination-pages-pane/destination-pages-pane'; + +import './destination-pages-dialog.scss'; + +export interface DestinationPagesDialogProps { + execution: Execution; + onClose(): void; +} + +export const DestinationPagesDialog = React.memo(function DestinationPagesDialog( + props: DestinationPagesDialogProps, +) { + const { execution, onClose } = props; + + return ( + +
+ +
+
+
+
+
+
+ ); +}); diff --git a/web-console/src/views/workbench-view/destination-pages-pane/destination-pages-pane.tsx b/web-console/src/views/workbench-view/destination-pages-pane/destination-pages-pane.tsx new file mode 100644 index 00000000000..20f68a8af69 --- /dev/null +++ b/web-console/src/views/workbench-view/destination-pages-pane/destination-pages-pane.tsx @@ -0,0 +1,135 @@ +/* + * 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. + */ + +import { AnchorButton, Button } from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; +import React from 'react'; +import ReactTable from 'react-table'; + +import type { Execution } from '../../../druid-models'; +import { Api, UrlBaser } from '../../../singletons'; +import { + clamp, + downloadUrl, + formatBytes, + formatInteger, + pluralIfNeeded, + wait, +} from '../../../utils'; + +const MAX_DETAIL_ROWS = 20; + +interface DestinationPagesPaneProps { + execution: Execution; +} + +export const DestinationPagesPane = React.memo(function DestinationPagesPane( + props: DestinationPagesPaneProps, +) { + const { execution } = props; + const destination = execution.destination; + const pages = execution.destinationPages; + if (!pages) return null; + const id = Api.encodePath(execution.id); + + const numTotalRows = destination?.numTotalRows; + + function getPageUrl(pageIndex: number) { + return UrlBaser.base(`/druid/v2/sql/statements/${id}/results?page=${pageIndex}`); + } + + function getPageFilename(pageIndex: number) { + return `${id}_page${pageIndex}.jsonl`; + } + + async function downloadAllPages() { + if (!pages) return; + for (let i = 0; i < pages.length; i++) { + downloadUrl(getPageUrl(i), getPageFilename(i)); + await wait(100); + } + } + + return ( +
+

+ {`${ + typeof numTotalRows === 'number' ? pluralIfNeeded(numTotalRows, 'row') : 'Results' + } have been written to ${pluralIfNeeded(pages.length, 'page')}. `} + {pages.length > 1 && ( +

+ ); +}); diff --git a/web-console/src/views/workbench-view/execution-details-pane/__snapshots__/execution-details-pane.spec.tsx.snap b/web-console/src/views/workbench-view/execution-details-pane/__snapshots__/execution-details-pane.spec.tsx.snap index fb8c7a0eff5..6f3c23bee00 100644 --- a/web-console/src/views/workbench-view/execution-details-pane/__snapshots__/execution-details-pane.spec.tsx.snap +++ b/web-console/src/views/workbench-view/execution-details-pane/__snapshots__/execution-details-pane.spec.tsx.snap @@ -23,6 +23,7 @@ exports[`ExecutionDetailsPane matches snapshot no init tab 1`] = ` "label": "Native query", }, undefined, + undefined, Object { "icon": "error", "id": "error", @@ -38,7 +39,10 @@ exports[`ExecutionDetailsPane matches snapshot no init tab 1`] = ` >

- General info for query-8f889312-e989-4b4c-9895-485a1fe796d3 ingesting into "kttm-blank-lines" + General info for query-af8a263d-213f-418e-ad8d-37d55beff59b ingesting into "kttm-blank-lines" +

+

+ Results written to dataSource

{`General info for ${execution.id}${ ingestDatasource ? ` ingesting into ${T(ingestDatasource)}` : '' }`}

+ {execution.destination && ( +

+ {`Results written to ${execution.destination.type}`} + {execution.destinationPages + ? ` (${pluralIfNeeded(execution.destinationPages.length, 'page')})` + : undefined} +

+ )} {execution.error && } {execution.stages ? ( ); + case 'pages': + if (!execution.destinationPages) return; + return ; + case 'error': return ; @@ -129,6 +151,11 @@ export const ExecutionDetailsPane = React.memo(function ExecutionDetailsPane( label: 'Results', icon: IconNames.TH, }, + execution.destinationPages && { + id: 'pages', + label: 'Result pages', + icon: IconNames.APPLICATIONS, + }, execution.error && { id: 'error', label: 'Error', diff --git a/web-console/src/views/workbench-view/execution-error-pane/__snapshots__/execution-error-pane.spec.tsx.snap b/web-console/src/views/workbench-view/execution-error-pane/__snapshots__/execution-error-pane.spec.tsx.snap index a57de9ed745..f6000ab38d4 100644 --- a/web-console/src/views/workbench-view/execution-error-pane/__snapshots__/execution-error-pane.spec.tsx.snap +++ b/web-console/src/views/workbench-view/execution-error-pane/__snapshots__/execution-error-pane.spec.tsx.snap @@ -21,7 +21,7 @@ exports[`ExecutionErrorPane matches snapshot 1`] = `
Failed task ID: (on host: diff --git a/web-console/src/views/workbench-view/execution-stages-pane/__snapshots__/execution-stages-pane.spec.tsx.snap b/web-console/src/views/workbench-view/execution-stages-pane/__snapshots__/execution-stages-pane.spec.tsx.snap index f28821b1b35..a5018aec85c 100644 --- a/web-console/src/views/workbench-view/execution-stages-pane/__snapshots__/execution-stages-pane.spec.tsx.snap +++ b/web-console/src/views/workbench-view/execution-stages-pane/__snapshots__/execution-stages-pane.spec.tsx.snap @@ -158,7 +158,7 @@ exports[`ExecutionStagesPane matches snapshot 1`] = ` Array [ Object { "definition": Object { - "id": "8af42220-2724-4a76-b39f-c2f98df2de69_0", + "id": "ad318360-2ccf-4afc-b221-27c8704bf4fe_0", "input": Array [ Object { "inputFormat": Object { @@ -196,15 +196,16 @@ exports[`ExecutionStagesPane matches snapshot 1`] = ` "context": Object { "__timeColumn": "v0", "__user": "allowAll", + "executionMode": "async", "finalize": false, "finalizeAggregations": false, "groupByEnableMultiValueUnnesting": false, "maxNumTasks": 2, "maxParseExceptions": 0, - "queryId": "5aa683e2-a6ee-4655-a834-a643e91055b1", + "queryId": "93a855fa-c35a-48df-b596-6bc98eed1101", "scanSignature": "[{\\"name\\":\\"agent_type\\",\\"type\\":\\"STRING\\"},{\\"name\\":\\"v0\\",\\"type\\":\\"LONG\\"}]", "sqlInsertSegmentGranularity": "{\\"type\\":\\"all\\"}", - "sqlQueryId": "5aa683e2-a6ee-4655-a834-a643e91055b1", + "sqlQueryId": "93a855fa-c35a-48df-b596-6bc98eed1101", "sqlReplaceTimeChunks": "all", }, "dataSource": Object { @@ -283,17 +284,17 @@ exports[`ExecutionStagesPane matches snapshot 1`] = ` }, ], }, - "duration": 20483, + "duration": 21324, "partitionCount": 1, "phase": "FINISHED", "sort": true, "stageNumber": 0, - "startTime": "2023-06-19T05:39:26.711Z", + "startTime": "2023-08-01T03:12:59.865Z", "workerCount": 1, }, Object { "definition": Object { - "id": "8af42220-2724-4a76-b39f-c2f98df2de69_1", + "id": "ad318360-2ccf-4afc-b221-27c8704bf4fe_1", "input": Array [ Object { "stage": 0, @@ -359,11 +360,11 @@ exports[`ExecutionStagesPane matches snapshot 1`] = ` }, "signature": Array [], }, - "duration": 2381, + "duration": 2070, "partitionCount": 1, "phase": "FINISHED", "stageNumber": 1, - "startTime": "2023-06-19T05:39:47.166Z", + "startTime": "2023-08-01T03:13:21.156Z", "workerCount": 1, }, ] diff --git a/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx b/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx index 182c47ff56c..046de0ffb5f 100644 --- a/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx +++ b/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx @@ -23,6 +23,7 @@ import AceEditor from 'react-ace'; import { Execution } from '../../../druid-models'; import { AppToaster } from '../../../singletons'; +import type { QueryDetailArchive } from '../../../utils'; import { offsetToRowColumn } from '../../../utils'; import './execution-submit-dialog.scss'; @@ -39,7 +40,7 @@ export const ExecutionSubmitDialog = React.memo(function ExecutionSubmitDialog( const [archive, setArchive] = useState(''); function handleSubmit(): void { - let parsed: any; + let parsed: QueryDetailArchive; try { parsed = JSONBig.parse(archive); } catch (e) { @@ -55,13 +56,13 @@ export const ExecutionSubmitDialog = React.memo(function ExecutionSubmitDialog( } let execution: Execution | undefined; - const detailArchiveVersion = parsed.detailArchiveVersion ?? parsed.profileVersion; + const detailArchiveVersion = parsed.detailArchiveVersion ?? (parsed as any).profileVersion; if (typeof detailArchiveVersion === 'number') { try { if (detailArchiveVersion === 2) { - execution = Execution.fromTaskReport(parsed.reports).updateWithTaskPayload( - parsed.payload, - ); + execution = Execution.fromTaskReport(parsed.reports) + .updateWithTaskPayload(parsed.payload) + .updateWithAsyncStatus(parsed.statementsStatus); } else { AppToaster.show({ intent: Intent.DANGER, @@ -76,9 +77,9 @@ export const ExecutionSubmitDialog = React.memo(function ExecutionSubmitDialog( }); return; } - } else if (typeof parsed.multiStageQuery === 'object') { + } else if (typeof (parsed as any).multiStageQuery === 'object') { try { - execution = Execution.fromTaskReport(parsed); + execution = Execution.fromTaskReport(parsed as any); } catch (e) { AppToaster.show({ intent: Intent.DANGER, diff --git a/web-console/src/views/workbench-view/execution-summary-panel/execution-summary-panel.tsx b/web-console/src/views/workbench-view/execution-summary-panel/execution-summary-panel.tsx index c98c94c4651..bdeb738196d 100644 --- a/web-console/src/views/workbench-view/execution-summary-panel/execution-summary-panel.tsx +++ b/web-console/src/views/workbench-view/execution-summary-panel/execution-summary-panel.tsx @@ -20,10 +20,11 @@ import { Button, ButtonGroup, Menu, MenuDivider, MenuItem, Position } from '@blu import { IconNames } from '@blueprintjs/icons'; import { Popover2 } from '@blueprintjs/popover2'; import type { JSX } from 'react'; -import React from 'react'; +import React, { useState } from 'react'; import type { Execution } from '../../../druid-models'; import { downloadQueryResults, formatDurationHybrid, pluralIfNeeded } from '../../../utils'; +import { DestinationPagesDialog } from '../destination-pages-dialog/destination-pages-dialog'; import './execution-summary-panel.scss'; @@ -37,6 +38,7 @@ export const ExecutionSummaryPanel = React.memo(function ExecutionSummaryPanel( props: ExecutionSummaryPanelProps, ) { const { execution, onExecutionDetail, onReset } = props; + const [showDestinationPages, setShowDestinationPages] = useState(false); const queryResult = execution?.result; const buttons: JSX.Element[] = []; @@ -71,21 +73,30 @@ export const ExecutionSummaryPanel = React.memo(function ExecutionSummaryPanel( } }} />, - - - handleDownload('csv')} /> - handleDownload('tsv')} /> - handleDownload('json')} /> - - } - position={Position.BOTTOM_RIGHT} - > -