From 4bd7320f7c17994f9df5e8d0d5c0e1cdcfe8cef6 Mon Sep 17 00:00:00 2001 From: Christopher Henn Date: Tue, 18 Jun 2019 14:48:33 -0700 Subject: [PATCH] fix(ui): update client version to resolve query bottleneck Closes #14084 Co-authored-by: Andrew Watkins --- CHANGELOG.md | 10 +++ ui/package-lock.json | 5 +- ui/package.json | 2 +- .../components/verifyStep/DataListening.tsx | 7 +- ui/src/shared/apis/query.ts | 66 +++++-------------- ui/src/shared/components/TimeSeries.tsx | 13 +--- ui/src/timeMachine/actions/queries.ts | 10 +-- ui/src/timeMachine/apis/queryBuilder.ts | 9 +-- ui/src/variables/utils/ValueFetcher.ts | 2 +- 9 files changed, 45 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2352b2510..d604c1085e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## v2.0.0-alpha.13 [unreleased] + +### Features + +### Bug Fixes + +### UI Improvements + +1. [14157](https://github.com/influxdata/influxdb/pull/14157): Remove rendering bottleneck when streaming Flux responses + ## v2.0.0-alpha.13 [2019-06-13] ### Features diff --git a/ui/package-lock.json b/ui/package-lock.json index 77acf71796..9c9449a7b9 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1088,8 +1088,9 @@ } }, "@influxdata/influx": { - "version": "github:influxdata/influxdb2-js#4bb7981498a2649391fbebdcaababafbf304f642", - "from": "github:influxdata/influxdb2-js#dev", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@influxdata/influx/-/influx-0.3.5.tgz", + "integrity": "sha512-D2sCbBBGAkEtWyOibfiCCLT2qnmUoeME7GSrMJ3yyuxM/YtY+/gGmDZGIih3OTQld+VvDMgD78yWb66nk4GPVA==", "requires": { "axios": "^0.19.0" } diff --git a/ui/package.json b/ui/package.json index 2821853ba8..1bbc27f85a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -140,7 +140,7 @@ }, "dependencies": { "@influxdata/clockface": "0.0.13", - "@influxdata/influx": "github:influxdata/influxdb2-js#dev", + "@influxdata/influx": "0.3.5", "@influxdata/influxdb-templates": "influxdata/influxdb-templates", "@influxdata/react-custom-scrollbars": "4.3.8", "@influxdata/giraffe": "0.12.1", diff --git a/ui/src/dataLoaders/components/verifyStep/DataListening.tsx b/ui/src/dataLoaders/components/verifyStep/DataListening.tsx index dab7e072b4..925effd0c8 100644 --- a/ui/src/dataLoaders/components/verifyStep/DataListening.tsx +++ b/ui/src/dataLoaders/components/verifyStep/DataListening.tsx @@ -118,19 +118,19 @@ class DataListening extends PureComponent { const script = `from(bucket: "${bucket}") |> range(start: -1m)` - let rowCount: number + let responseLength: number let timePassed: number try { const response = await runQuery(orgID, script).promise - rowCount = response.rowCount + responseLength = response.length timePassed = Number(new Date()) - this.startTime } catch (err) { this.setState({loading: LoadingState.Error}) return } - if (rowCount > 1) { + if (responseLength > 1) { this.setState({loading: LoadingState.Done}) return } @@ -139,6 +139,7 @@ class DataListening extends PureComponent { this.setState({loading: LoadingState.NotFound}) return } + this.intervalID = setTimeout(this.checkForData, FETCH_WAIT) } diff --git a/ui/src/shared/apis/query.ts b/ui/src/shared/apis/query.ts index 12b1967034..73b8885140 100644 --- a/ui/src/shared/apis/query.ts +++ b/ui/src/shared/apis/query.ts @@ -1,68 +1,36 @@ -import Deferred from 'src/utils/Deferred' import {getWindowVars} from 'src/variables/utils/getWindowVars' import {buildVarsOption} from 'src/variables/utils/buildVarsOption' import {client} from 'src/utils/api' -import {File} from '@influxdata/influx' +import { + File, + CancellationError as ClientCancellationError, +} from '@influxdata/influx' // Types import {WrappedCancelablePromise, CancellationError} from 'src/types/promises' import {VariableAssignment} from 'src/types/ast' -const MAX_ROWS = 50000 - -export interface ExecuteFluxQueryResult { - csv: string - didTruncate: boolean - rowCount: number -} +const MAX_RESPONSE_CHARS = 50000 * 160 export const runQuery = ( orgID: string, query: string, extern?: File -): WrappedCancelablePromise => { - const deferred = new Deferred() - - const conn = client.queries.execute(orgID, query, extern) - - let didTruncate = false - let rowCount = 0 - let csv = '' - - conn.stream.on('data', d => { - rowCount++ - csv += d - - if (rowCount < MAX_ROWS) { - return - } - - didTruncate = true - conn.cancel() +): WrappedCancelablePromise => { + const {promise, cancel} = client.queries.execute(orgID, query, { + extern, + limitChars: MAX_RESPONSE_CHARS, }) - conn.stream.on('end', () => { - const result: ExecuteFluxQueryResult = { - csv, - didTruncate, - rowCount, - } + // Convert the client `CancellationError` to a UI `CancellationError` + const wrappedPromise = promise.catch(error => + error instanceof ClientCancellationError + ? Promise.reject(CancellationError) + : Promise.reject(error) + ) - deferred.resolve(result) - }) - - conn.stream.on('error', err => { - deferred.reject(err) - }) - - return { - promise: deferred.promise, - cancel: () => { - conn.cancel() - deferred.reject(new CancellationError()) - }, - } + return {promise: wrappedPromise, cancel} } /* @@ -86,7 +54,7 @@ export const executeQueryWithVars = ( orgID: string, query: string, variables?: VariableAssignment[] -): WrappedCancelablePromise => { +): WrappedCancelablePromise => { let isCancelled = false let cancelExecution diff --git a/ui/src/shared/components/TimeSeries.tsx b/ui/src/shared/components/TimeSeries.tsx index 098286e7df..bb7d062313 100644 --- a/ui/src/shared/components/TimeSeries.tsx +++ b/ui/src/shared/components/TimeSeries.tsx @@ -6,10 +6,7 @@ import {withRouter, WithRouterProps} from 'react-router' import {fromFlux, FromFluxResult} from '@influxdata/giraffe' // API -import { - executeQueryWithVars, - ExecuteFluxQueryResult, -} from 'src/shared/apis/query' +import {executeQueryWithVars} from 'src/shared/apis/query' // Utils import {checkQueryResult} from 'src/shared/utils/checkQueryResult' @@ -81,9 +78,7 @@ class TimeSeries extends Component { public state: State = defaultState() - private pendingResults: Array< - WrappedCancelablePromise - > = [] + private pendingResults: Array> = [] public async componentDidMount() { this.reload() @@ -144,10 +139,8 @@ class TimeSeries extends Component { ) // Wait for new queries to complete - const results = await Promise.all(this.pendingResults.map(r => r.promise)) - + const files = await Promise.all(this.pendingResults.map(r => r.promise)) const duration = Date.now() - startTime - const files = results.map(r => r.csv) const giraffeResult = fromFlux(files.join('\n\n')) files.forEach(checkQueryResult) diff --git a/ui/src/timeMachine/actions/queries.ts b/ui/src/timeMachine/actions/queries.ts index 828c8a805a..76effec1d4 100644 --- a/ui/src/timeMachine/actions/queries.ts +++ b/ui/src/timeMachine/actions/queries.ts @@ -1,10 +1,7 @@ import {get} from 'lodash' // API -import { - executeQueryWithVars, - ExecuteFluxQueryResult, -} from 'src/shared/apis/query' +import {executeQueryWithVars} from 'src/shared/apis/query' // Actions import {refreshVariableValues, selectValue} from 'src/variables/actions' @@ -85,7 +82,7 @@ export const refreshTimeMachineVariableValues = () => async ( await dispatch(refreshVariableValues(contextID, variablesToRefresh)) } -let pendingResults: Array> = [] +let pendingResults: Array> = [] export const executeQueries = () => async (dispatch, getState: GetState) => { const {view, timeRange} = getActiveTimeMachine(getState()) @@ -115,10 +112,9 @@ export const executeQueries = () => async (dispatch, getState: GetState) => { executeQueryWithVars(orgID, text, variableAssignments) ) - const results = await Promise.all(pendingResults.map(r => r.promise)) + const files = await Promise.all(pendingResults.map(r => r.promise)) const duration = Date.now() - startTime - const files = results.map(r => r.csv) files.forEach(checkQueryResult) diff --git a/ui/src/timeMachine/apis/queryBuilder.ts b/ui/src/timeMachine/apis/queryBuilder.ts index 947f5f6d34..77d10d41b2 100644 --- a/ui/src/timeMachine/apis/queryBuilder.ts +++ b/ui/src/timeMachine/apis/queryBuilder.ts @@ -2,7 +2,7 @@ import {get} from 'lodash' // APIs -import {runQuery, ExecuteFluxQueryResult} from 'src/shared/apis/query' +import {runQuery} from 'src/shared/apis/query' import {parseResponse} from 'src/shared/parsing/flux/response' // Utils @@ -122,11 +122,8 @@ export function findValues({ } } -export function extractCol( - resp: ExecuteFluxQueryResult, - colName: string -): string[] { - const tables = parseResponse(resp.csv) +export function extractCol(resp: string, colName: string): string[] { + const tables = parseResponse(resp) const data = get(tables, '0.data', []) if (!data.length) { diff --git a/ui/src/variables/utils/ValueFetcher.ts b/ui/src/variables/utils/ValueFetcher.ts index 2b69b698df..d53b970f4b 100644 --- a/ui/src/variables/utils/ValueFetcher.ts +++ b/ui/src/variables/utils/ValueFetcher.ts @@ -87,7 +87,7 @@ export class DefaultValueFetcher implements ValueFetcher { const request = executeQueryWithVars(orgID, query, variables) - const promise = request.promise.then(({csv}) => { + const promise = request.promise.then(csv => { const values = extractValues(csv, prevSelection, defaultSelection) this.cache[key] = values