Skip to content

Commit

Permalink
feat(cache-cell-results): added cell result caching to reduce cell co…
Browse files Browse the repository at this point in the history
…nfiguration querying on loads (#18581)
  • Loading branch information
asalem1 authored Jun 23, 2020
1 parent 81e4b02 commit c4fee5b
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
1. [18623](https://github.com/influxdata/influxdb/pull/18623): Drop support for --local flag within influx CLI
1. [18632](https://github.com/influxdata/influxdb/pull/18632): Prevents undefined queries in cells from erroring out in dashboards
1. [18658](https://github.com/influxdata/influxdb/pull/18658): Add support for 'd' day time identifier in the CLI for bucket and setup commands
1. [18581](https://github.com/influxdata/influxdb/pull/18581): Cache dashboard cell query results to use as a reference for cell configurations

## v2.0.0-beta.12 [2020-06-12]

Expand Down
21 changes: 17 additions & 4 deletions ui/src/dashboards/components/DashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@ import RateLimitAlert from 'src/cloud/components/RateLimitAlert'
// Utils
import {pageTitleSuffixer} from 'src/shared/utils/pageTitles'

// Selectors
// Selectors & Actions
import {resetCachedQueryResults} from 'src/queryCache/actions'
import {getByID} from 'src/resources/selectors'

// Types
import {AppState, AutoRefresh, ResourceType, Dashboard} from 'src/types'
import {ManualRefreshProps} from 'src/shared/components/ManualRefresh'

interface DispatchProps {
resetCachedQueryResults: typeof resetCachedQueryResults
}

interface StateProps {
dashboard: Dashboard
}
Expand All @@ -31,10 +36,14 @@ interface OwnProps {
autoRefresh: AutoRefresh
}

type Props = OwnProps & StateProps & ManualRefreshProps
type Props = OwnProps & StateProps & ManualRefreshProps & DispatchProps

@ErrorHandling
class DashboardPage extends Component<Props> {
public componentWillUnmount() {
this.props.resetCachedQueryResults()
}

public render() {
const {autoRefresh, manualRefresh, onManualRefresh, children} = this.props

Expand Down Expand Up @@ -76,7 +85,11 @@ const mstp = (state: AppState): StateProps => {
}
}

export default connect<StateProps, {}>(
const mdtp = {
resetCachedQueryResults: resetCachedQueryResults,
}

export default connect<StateProps, DispatchProps>(
mstp,
null
mdtp
)(ManualRefresh<OwnProps>(DashboardPage))
13 changes: 6 additions & 7 deletions ui/src/dashboards/components/EditVEO.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import VEOHeader from 'src/dashboards/components/VEOHeader'
// Actions
import {setName} from 'src/timeMachine/actions'
import {saveVEOView} from 'src/dashboards/actions/thunks'
import {getViewForTimeMachine} from 'src/views/actions/thunks'
import {getViewAndResultsForVEO} from 'src/views/actions/thunks'

// Utils
import {getActiveTimeMachine} from 'src/timeMachine/selectors'
Expand All @@ -21,21 +21,21 @@ import {getActiveTimeMachine} from 'src/timeMachine/selectors'
import {AppState, RemoteDataState, QueryView, TimeMachineID} from 'src/types'

interface DispatchProps {
getViewAndResultsForVEO: typeof getViewAndResultsForVEO
onSetName: typeof setName
onSaveView: typeof saveVEOView
getViewForTimeMachine: typeof getViewForTimeMachine
}

interface StateProps {
view: QueryView | null
activeTimeMachineID: TimeMachineID
view: QueryView | null
}

type Props = DispatchProps & StateProps & WithRouterProps

const EditViewVEO: FunctionComponent<Props> = ({
getViewForTimeMachine,
activeTimeMachineID,
getViewAndResultsForVEO,
onSaveView,
onSetName,
params: {orgID, cellID, dashboardID},
Expand All @@ -46,7 +46,7 @@ const EditViewVEO: FunctionComponent<Props> = ({
// TODO split this up into "loadView" "setActiveTimeMachine"
// and something to tell the component to pull from the context
// of the dashboardID
getViewForTimeMachine(dashboardID, cellID, 'veo')
getViewAndResultsForVEO(dashboardID, cellID, 'veo')
}, [])

const handleClose = () => {
Expand Down Expand Up @@ -92,16 +92,15 @@ const EditViewVEO: FunctionComponent<Props> = ({

const mstp = (state: AppState): StateProps => {
const {activeTimeMachineID} = state.timeMachines

const {view} = getActiveTimeMachine(state)

return {view, activeTimeMachineID}
}

const mdtp: DispatchProps = {
getViewAndResultsForVEO: getViewAndResultsForVEO,
onSetName: setName,
onSaveView: saveVEOView,
getViewForTimeMachine: getViewForTimeMachine,
}

export default connect<StateProps, DispatchProps, {}>(
Expand Down
33 changes: 33 additions & 0 deletions ui/src/queryCache/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export type Action =
| ReturnType<typeof resetCachedQueryResults>
| ReturnType<typeof setQueryResultsByQueryID>

// Hashing function found here:
// https://jsperf.com/hashcodelordvlad
// Through this thread:
// https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript
export const hashCode = (queryText: string): string => {
let hash = 0,
char
if (!queryText) {
return `${hash}`
}
for (let i = 0; i < queryText.length; i++) {
char = queryText.charCodeAt(i)
hash = (hash << 5) - hash + char
hash |= 0 // Convert to 32bit integer
}
return `${hash}`
}

export const setQueryResultsByQueryID = (queryID: string, files: string[]) =>
({
type: 'SET_QUERY_RESULTS_BY_QUERY',
queryID,
files,
} as const)

export const resetCachedQueryResults = () =>
({
type: 'RESET_CACHED_QUERY_RESULTS',
} as const)
33 changes: 33 additions & 0 deletions ui/src/queryCache/reducers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Libraries
import {produce} from 'immer'

// Actions
import {Action} from 'src/queryCache/actions'

export interface QueryCacheState {
queryResultsByQueryID: {[queryID: string]: string[]}
}

export const initialState: QueryCacheState = {
queryResultsByQueryID: {},
}

export const queryCacheReducer = (
state: QueryCacheState = initialState,
action: Action
): QueryCacheState => {
switch (action.type) {
case 'SET_QUERY_RESULTS_BY_QUERY': {
return produce(state, draftState => {
const {queryID, files} = action
draftState.queryResultsByQueryID[queryID] = files
})
}

case 'RESET_CACHED_QUERY_RESULTS': {
return {queryResultsByQueryID: {}}
}
}

return state
}
17 changes: 16 additions & 1 deletion ui/src/shared/components/TimeSeries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
isDemoDataAvailabilityError,
demoDataError,
} from 'src/cloud/utils/demoDataErrors'
import {hashCode} from 'src/queryCache/actions'

// Constants
import {
Expand All @@ -43,6 +44,7 @@ import {TIME_RANGE_START, TIME_RANGE_STOP} from 'src/variables/constants'

// Actions
import {notify as notifyAction} from 'src/shared/actions/notifications'
import {setQueryResultsByQueryID} from 'src/queryCache/actions'

// Types
import {
Expand Down Expand Up @@ -87,6 +89,7 @@ interface OwnProps {

interface DispatchProps {
notify: typeof notifyAction
onSetQueryResultsByQueryID: typeof setQueryResultsByQueryID
}

type Props = StateProps & OwnProps & DispatchProps
Expand Down Expand Up @@ -181,7 +184,13 @@ class TimeSeries extends Component<Props & WithRouterProps, State> {
}

private reload = async () => {
const {variables, notify, check, buckets} = this.props
const {
buckets,
check,
notify,
onSetQueryResultsByQueryID,
variables,
} = this.props
const queries = this.props.queries.filter(({text}) => !!text.trim())

if (!queries.length) {
Expand Down Expand Up @@ -270,6 +279,11 @@ class TimeSeries extends Component<Props & WithRouterProps, State> {
}

this.pendingReload = false
const queryText = queries.map(({text}) => text).join('')
const queryID = hashCode(queryText)
if (queryID && files.length) {
onSetQueryResultsByQueryID(queryID, files)
}

this.setState({
giraffeResult,
Expand Down Expand Up @@ -346,6 +360,7 @@ const mstp = (state: AppState, props: OwnProps): StateProps => {

const mdtp: DispatchProps = {
notify: notifyAction,
onSetQueryResultsByQueryID: setQueryResultsByQueryID,
}

export default connect<StateProps, {}, OwnProps>(
Expand Down
2 changes: 2 additions & 0 deletions ui/src/store/configureStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import alertBuilderReducer from 'src/alerting/reducers/alertBuilder'

// Types
import {AppState, LocalStorage} from 'src/types'
import {queryCacheReducer} from 'src/queryCache/reducers'

type ReducerState = Pick<AppState, Exclude<keyof AppState, 'timeRange'>>

Expand All @@ -81,6 +82,7 @@ export const rootReducer = combineReducers<ReducerState>({
overlays: overlaysReducer,
plugins: pluginsResourceReducer,
predicates: predicatesReducer,
queryCache: queryCacheReducer,
ranges: rangesReducer,
resources: combineReducers({
buckets: bucketsReducer,
Expand Down
9 changes: 5 additions & 4 deletions ui/src/timeMachine/actions/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ interface SetQueryResults {
}
}

const setQueryResults = (
export const setQueryResults = (
status: RemoteDataState,
files?: string[],
fetchDuration?: number,
Expand Down Expand Up @@ -125,9 +125,6 @@ export const executeQueries = (abortController?: AbortController) => async (
const queries = activeTimeMachine.view.properties.queries.filter(
({text}) => !!text.trim()
)
const {
alertBuilder: {id: checkID},
} = state

if (!queries.length) {
dispatch(setQueryResults(RemoteDataState.Done, [], null))
Expand Down Expand Up @@ -177,6 +174,10 @@ export const executeQueries = (abortController?: AbortController) => async (
)

let statuses = [[]] as StatusRow[][]
const {
alertBuilder: {id: checkID},
} = state

if (checkID) {
const extern = buildVarsOption(variableAssignments)
pendingCheckStatuses = runStatusesQuery(getOrg(state).id, checkID, extern)
Expand Down
16 changes: 10 additions & 6 deletions ui/src/types/stores.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import {Links} from 'src/types/links'
import {Notification} from 'src/types'
import {TimeRange} from 'src/types/queries'
import {TimeMachinesState} from 'src/timeMachine/reducers'
import {AppState as AppPresentationState} from 'src/shared/reducers/app'
import {RouterState} from 'react-router-redux'
Expand All @@ -10,7 +7,14 @@ import {CurrentDashboardState} from 'src/shared/reducers/currentDashboard'
import {NoteEditorState} from 'src/dashboards/reducers/notes'
import {DataLoadingState} from 'src/dataLoaders/reducers'
import {OnboardingState} from 'src/onboarding/reducers'
import {PredicatesState, VariableEditorState} from 'src/types'
import {
Links,
Notification,
PredicatesState,
ResourceState,
TimeRange,
VariableEditorState,
} from 'src/types'
import {
TelegrafEditorPluginState,
PluginResourceState,
Expand All @@ -26,8 +30,7 @@ import {AlertBuilderState} from 'src/alerting/reducers/alertBuilder'
import {CurrentPage} from 'src/shared/reducers/currentPage'
import {DemoDataState} from 'src/cloud/reducers/demodata'
import {OrgSettingsState} from 'src/cloud/reducers/orgsettings'

import {ResourceState} from 'src/types'
import {QueryCacheState} from 'src/queryCache/reducers'

export interface AppState {
alertBuilder: AlertBuilderState
Expand All @@ -40,6 +43,7 @@ export interface AppState {
}
currentPage: CurrentPage
currentDashboard: CurrentDashboardState
queryCache: QueryCacheState
dataLoading: DataLoadingState
links: Links
me: MeState
Expand Down
22 changes: 20 additions & 2 deletions ui/src/views/actions/thunks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Libraries
import {normalize} from 'normalizr'

import {get} from 'lodash'
// APIs
import {
getView as getViewAJAX,
Expand All @@ -16,6 +16,8 @@ import {notify} from 'src/shared/actions/notifications'
import {setActiveTimeMachine} from 'src/timeMachine/actions'
import {executeQueries} from 'src/timeMachine/actions/queries'
import {setView, Action} from 'src/views/actions/creators'
import {hashCode} from 'src/queryCache/actions'
import {setQueryResults} from 'src/timeMachine/actions/queries'

// Selectors
import {getViewsForDashboard} from 'src/views/selectors'
Expand Down Expand Up @@ -96,7 +98,7 @@ export const updateViewAndVariables = (
}
}

export const getViewForTimeMachine = (
export const getViewAndResultsForVEO = (
dashboardID: string,
cellID: string,
timeMachineID: TimeMachineID
Expand All @@ -116,8 +118,24 @@ export const getViewForTimeMachine = (
view,
})
)
const queries = view.properties.queries.filter(({text}) => !!text.trim())
if (!queries.length) {
dispatch(setQueryResults(RemoteDataState.Done, [], null))
}
const queryText = queries.map(({text}) => text).join('')
const queryID = hashCode(queryText)
const files = get(
state,
['queryCache', 'queryResultsByQueryID', queryID],
undefined
)
if (files) {
dispatch(setQueryResults(RemoteDataState.Done, files, null, null))
return
}
dispatch(executeQueries())
} catch (error) {
console.error(error)
dispatch(notify(copy.getViewFailed(error.message)))
dispatch(setView(cellID, RemoteDataState.Error))
}
Expand Down

0 comments on commit c4fee5b

Please sign in to comment.