Further improve rendering performance

This commit is contained in:
Aleksandar Petrov
2023-11-29 18:04:17 -04:00
parent 7b6e383552
commit 747f89bf48
5 changed files with 51 additions and 30 deletions
@@ -12,12 +12,12 @@ interface Props {
value: string;
onChange: (val: string) => void;
onRunQuery: (value: string) => void;
labels?: string[];
getLabelNames: () => string[];
getLabelValues: (label: string) => Promise<string[]>;
}
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<string[]>, labels?: string[]) {
function useAutocomplete(getLabelValues: (label: string) => Promise<string[]>, getLabelNames: () => string[]) {
const providerRef = useRef<CompletionProvider>();
if (providerRef.current === undefined) {
providerRef.current = new CompletionProvider();
@@ -100,9 +100,9 @@ function useAutocomplete(getLabelValues: (label: string) => Promise<string[]>, 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(() => {
@@ -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 = <LoadingPlaceholder text={'Loading'} />;
@@ -55,13 +54,13 @@ export function QueryEditor(props: Props) {
value={query.labelSelector}
onChange={onLabelSelectorChange}
onRunQuery={handleRunQuery}
labels={labels}
getLabelNames={getLabelNames}
getLabelValues={getLabelValues}
/>
<PyroscopeQueryLinkExtensions {...props} />
</EditorRow>
<EditorRow>
<QueryOptions query={query} onQueryChange={props.onChange} app={props.app} labels={labels} />
<QueryOptions query={query} onQueryChange={props.onChange} app={props.app} getLabelNames={getLabelNames} />
</EditorRow>
</EditorRows>
);
@@ -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 };
}
@@ -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,
}))
@@ -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([]);
}
@@ -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<string[]> = () => Promise.resolve([]);
init(labels: string[], getLabelValues: (label: string) => Promise<string[]>) {
this.labels = labels;
init(getLabelNames: () => string[], getLabelValues: (label: string) => Promise<string[]>) {
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,