From 747f89bf486d752bf87f217b0e561c5278424b93 Mon Sep 17 00:00:00 2001 From: Aleksandar Petrov <8142643+aleks-p@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:01:08 -0400 Subject: [PATCH] Further improve rendering performance --- .../QueryEditor/LabelsEditor.tsx | 10 ++-- .../QueryEditor/QueryEditor.tsx | 51 +++++++++++++------ .../QueryEditor/QueryOptions.tsx | 8 +-- .../QueryEditor/autocomplete.test.ts | 2 +- .../QueryEditor/autocomplete.ts | 10 ++-- 5 files changed, 51 insertions(+), 30 deletions(-) diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/LabelsEditor.tsx b/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/LabelsEditor.tsx index 6c7cee86081..32b6c76cd15 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/LabelsEditor.tsx +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/LabelsEditor.tsx @@ -12,12 +12,12 @@ interface Props { value: string; onChange: (val: string) => void; onRunQuery: (value: string) => void; - labels?: string[]; + getLabelNames: () => string[]; getLabelValues: (label: string) => Promise; } export function LabelsEditor(props: Props) { - const setupAutocompleteFn = useAutocomplete(props.getLabelValues, props.labels); + const setupAutocompleteFn = useAutocomplete(props.getLabelValues, props.getLabelNames); const styles = useStyles2(getStyles); const onRunQueryRef = useLatest(props.onRunQuery); @@ -92,7 +92,7 @@ const EDITOR_HEIGHT_OFFSET = 2; /** * Hook that returns function that will set up monaco autocomplete for the label selector */ -function useAutocomplete(getLabelValues: (label: string) => Promise, labels?: string[]) { +function useAutocomplete(getLabelValues: (label: string) => Promise, getLabelNames: () => string[]) { const providerRef = useRef(); if (providerRef.current === undefined) { providerRef.current = new CompletionProvider(); @@ -100,9 +100,9 @@ function useAutocomplete(getLabelValues: (label: string) => Promise, l useAsync(async () => { if (providerRef.current) { - providerRef.current.init(labels || [], getLabelValues); + providerRef.current.init(getLabelNames, getLabelValues); } - }, [labels, getLabelValues]); + }, [getLabelNames, getLabelValues]); const autocompleteDisposeFun = useRef<(() => void) | null>(null); useEffect(() => { diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryEditor.tsx b/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryEditor.tsx index d689f6dda9d..532f2e5fce6 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryEditor.tsx +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryEditor.tsx @@ -1,6 +1,5 @@ import deepEqual from 'fast-deep-equal'; -import React, {useCallback, useEffect, useMemo} from 'react'; -import { useAsync } from 'react-use'; +import React, {useCallback, useEffect, useState} from 'react'; import { CoreApp, QueryEditorProps, TimeRange } from '@grafana/data'; import { LoadingPlaceholder } from '@grafana/ui'; @@ -26,7 +25,7 @@ export function QueryEditor(props: Props) { } const profileTypes = useProfileTypes(datasource); - const { labels, getLabelValues, onLabelSelectorChange } = useLabels(range, datasource, query, onChange); + const { getLabelNames, getLabelValues, onLabelSelectorChange } = useLabels(range, datasource, query, onChange); useNormalizeQuery(query, profileTypes, onChange, app); let cascader = ; @@ -55,13 +54,13 @@ export function QueryEditor(props: Props) { value={query.labelSelector} onChange={onLabelSelectorChange} onRunQuery={handleRunQuery} - labels={labels} + getLabelNames={getLabelNames} getLabelValues={getLabelValues} /> - + ); @@ -136,20 +135,38 @@ function useLabels( return `{${labels.join(',')}}` } - const labelSelector = useMemo(() => createSelector(query.labelSelector, query.profileTypeId, '') - , [query.labelSelector, query.profileTypeId]); + const [queryLabels, setQueryLabels] = useState(() => ({ labels: [] as string[] })); + const [processedLabelSelector, setProcessedLabelSelector] = useState(() => ({ labelSelector: createSelector('', query.profileTypeId, '') })); + const [rawQuery, setRawQuery] = useState(() => ( { data: ''})) + + useEffect(() => { + setProcessedLabelSelector({ + labelSelector: createSelector(rawQuery.data, query.profileTypeId, ''), + }); + }, [rawQuery.data, query.profileTypeId]); const getLabelNames = useCallback( - () => datasource.getLabelNames(labelSelector, unpreciseRange.from, unpreciseRange.to), - [datasource, labelSelector, unpreciseRange.from, unpreciseRange.to] + () => queryLabels.labels, + [queryLabels] ); - const labelsResult = useAsync(getLabelNames, [getLabelNames]); + useEffect(() => { + const fetchData = async() => { + const labels = await datasource.getLabelNames( + processedLabelSelector.labelSelector, + unpreciseRange.from, + unpreciseRange.to + ); + + setQueryLabels((prevQueryLabels) => ({ ...prevQueryLabels, labels })); + } + fetchData(); + }, [processedLabelSelector.labelSelector, unpreciseRange.from, unpreciseRange.to, datasource, setQueryLabels]); // Create a function with range and query already baked in so we don't have to send those everywhere const getLabelValues = useCallback( (label: string) => { - let labelSelector = createSelector(query.labelSelector, query.profileTypeId, label); + let labelSelector = createSelector(rawQuery.data, query.profileTypeId, label); console.log(labelSelector) const labelValues = datasource.getLabelValues( labelSelector, @@ -160,15 +177,19 @@ function useLabels( console.log(labelValues) return labelValues; }, - [datasource, query, unpreciseRange.to, unpreciseRange.from] + [datasource, rawQuery.data, query.profileTypeId, unpreciseRange.to, unpreciseRange.from] ); const onLabelSelectorChange = useCallback( (value: string) => { - onChange({ ...query, labelSelector: value }); + // onChange({ ...query, labelSelector: value }); + setRawQuery({ + data: value, + }) + query.labelSelector = value }, - [onChange, query] + [setRawQuery, query] ); - return { labels: labelsResult.value, getLabelValues, onLabelSelectorChange }; + return { getLabelNames, getLabelValues, onLabelSelectorChange }; } diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryOptions.tsx b/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryOptions.tsx index 94ade2699b3..e2a90abbec5 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryOptions.tsx +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryOptions.tsx @@ -15,7 +15,7 @@ export interface Props { query: Query; onQueryChange: (query: Query) => void; app?: CoreApp; - labels?: string[]; + getLabelNames: () => string[]; } const typeOptions: Array<{ value: Query['queryType']; label: string; description: string }> = [ @@ -34,11 +34,11 @@ function getTypeOptions(app?: CoreApp) { /** * Base on QueryOptionGroup component from grafana/ui but that is not available yet. */ -export function QueryOptions({ query, onQueryChange, app, labels }: Props) { +export function QueryOptions({ query, onQueryChange, app, getLabelNames }: Props) { const styles = useStyles2(getStyles); const typeOptions = getTypeOptions(app); - const groupByOptions = labels - ? labels.map((l) => ({ + const groupByOptions = getLabelNames() + ? getLabelNames().map((l) => ({ label: l, value: l, })) diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/autocomplete.test.ts b/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/autocomplete.test.ts index 2ea8ddb29cb..27e66dc85e9 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/autocomplete.test.ts +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/autocomplete.test.ts @@ -46,7 +46,7 @@ const defaultLabels = ['foo']; function setup(value: string, offset: number, labels: string[] = []) { const provider = new CompletionProvider(); - provider.init(labels, (label) => { + provider.init(() => labels, (label) => { if (labels.length === 0) { return Promise.resolve([]); } diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/autocomplete.ts b/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/autocomplete.ts index 688dda34f27..1caca498c86 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/autocomplete.ts +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/autocomplete.ts @@ -14,11 +14,11 @@ export class CompletionProvider implements monacoTypes.languages.CompletionItemP monaco: Monaco | undefined; editor: monacoTypes.editor.IStandaloneCodeEditor | undefined; - private labels: string[] = []; + private getLabelNames: () => string[] = () => []; private getLabelValues: (label: string) => Promise = () => Promise.resolve([]); - init(labels: string[], getLabelValues: (label: string) => Promise) { - this.labels = labels; + init(getLabelNames: () => string[], getLabelValues: (label: string) => Promise) { + this.getLabelNames = getLabelNames; this.getLabelValues = getLabelValues; } @@ -68,7 +68,7 @@ export class CompletionProvider implements monacoTypes.languages.CompletionItemP return []; } case 'EMPTY': { - return this.labels.map((key) => { + return this.getLabelNames().map((key) => { return { label: key, insertText: `{${key}="`, @@ -77,7 +77,7 @@ export class CompletionProvider implements monacoTypes.languages.CompletionItemP }); } case 'IN_LABEL_NAME': - return this.labels.map((key) => { + return this.getLabelNames().map((key) => { return { label: key, insertText: key,