Skip to content

Commit 130a86d

Browse files
authored
Explore metrics: Improve performance for build layout (grafana#92238)
* revert buildLayout * filter metric names using metricPrefix using regex * build groups with all the metric names and only build them once * remove commented code * use the metrics search input results to build the prefix select options * simplify prefix regex because we do not have to do it at the same time as the metrics search input regex
1 parent 2ad9d8c commit 130a86d

File tree

1 file changed

+45
-50
lines changed

1 file changed

+45
-50
lines changed

public/app/features/trails/MetricSelect/MetricSelectScene.tsx

Lines changed: 45 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import {
2929
import { Alert, Field, Icon, IconButton, InlineSwitch, Input, Select, Tooltip, useStyles2 } from '@grafana/ui';
3030
import { Trans } from 'app/core/internationalization';
3131

32-
import { DataTrail } from '../DataTrail';
3332
import { MetricScene } from '../MetricScene';
3433
import { StatusWrapper } from '../StatusWrapper';
3534
import { Node, Parser } from '../groop/parser';
@@ -214,18 +213,32 @@ export class MetricSelectScene extends SceneObjectBase<MetricSelectSceneState> i
214213
try {
215214
const response = await getMetricNames(datasourceUid, timeRange, match, MAX_METRIC_NAMES);
216215
const searchRegex = createJSRegExpFromSearchTerms(getMetricSearch(this));
217-
const metricNames = searchRegex
216+
let metricNames = searchRegex
218217
? response.data.filter((metric) => !searchRegex || searchRegex.test(metric))
219218
: response.data;
220219

220+
// use this to generate groups for metric prefix
221+
const filteredMetricNames = metricNames;
222+
223+
// filter the remaining metrics with the metric prefix
224+
const metricPrefix = this.state.metricPrefix;
225+
if (metricPrefix && metricPrefix !== 'all') {
226+
const prefixRegex = new RegExp(`(^${metricPrefix}.*)`, 'igy');
227+
metricNames = metricNames.filter((metric) => !prefixRegex || prefixRegex.test(metric));
228+
}
229+
221230
const metricNamesWarning = response.limitReached
222231
? `This feature will only return up to ${MAX_METRIC_NAMES} metric names for performance reasons. ` +
223232
`This limit is being exceeded for the current data source. ` +
224233
`Add search terms or label filters to narrow down the number of metric names returned.`
225234
: undefined;
226235

227236
let bodyLayout = this.state.body;
228-
const rootGroupNode = await this.generateGroups(metricNames);
237+
238+
let rootGroupNode = this.state.rootGroup;
239+
240+
// generate groups based on the search metrics input
241+
rootGroupNode = await this.generateGroups(filteredMetricNames);
229242

230243
this.setState({
231244
metricNames,
@@ -311,74 +324,54 @@ export class MetricSelectScene extends SceneObjectBase<MetricSelectSceneState> i
311324
this.buildLayout();
312325
}
313326

327+
private sortedPreviewMetrics() {
328+
return Object.values(this.previewCache).sort((a, b) => {
329+
if (a.isEmpty && b.isEmpty) {
330+
return a.index - b.index;
331+
}
332+
if (a.isEmpty) {
333+
return 1;
334+
}
335+
if (b.isEmpty) {
336+
return -1;
337+
}
338+
return a.index - b.index;
339+
});
340+
}
341+
314342
private async buildLayout() {
315343
// Temp hack when going back to select metric scene and variable updates
316344
if (this.ignoreNextUpdate) {
317345
this.ignoreNextUpdate = false;
318346
return;
319347
}
320348

321-
if (!this.state.rootGroup) {
322-
const rootGroupNode = await this.generateGroups(this.state.metricNames);
323-
this.setState({ rootGroup: rootGroupNode });
324-
}
325-
326-
const children = await this.populateFilterableViewLayout();
327-
const rowTemplate = this.state.showPreviews ? ROW_PREVIEW_HEIGHT : ROW_CARD_HEIGHT;
328-
this.state.body.setState({ children, autoRows: rowTemplate });
329-
}
349+
const children: SceneFlexItem[] = [];
330350

331-
private async populateFilterableViewLayout() {
332351
const trail = getTrailFor(this);
352+
353+
const metricsList = this.sortedPreviewMetrics();
354+
333355
// Get the current filters to determine the count of them
334356
// Which is required for `getPreviewPanelFor`
335357
const filters = getFilters(this);
336-
337-
let rootGroupNode = this.state.rootGroup;
338-
if (!rootGroupNode) {
339-
rootGroupNode = await this.generateGroups(this.state.metricNames);
340-
this.setState({ rootGroup: rootGroupNode });
341-
}
342-
343-
const children: SceneFlexItem[] = [];
344-
345-
for (const [groupKey, groupNode] of rootGroupNode.groups) {
346-
if (this.state.metricPrefix !== METRIC_PREFIX_ALL && this.state.metricPrefix !== groupKey) {
347-
continue;
348-
}
349-
350-
for (const [_, value] of groupNode.groups) {
351-
const panels = await this.populatePanels(trail, filters, value.values);
352-
children.push(...panels);
353-
}
354-
355-
const morePanelsMaybe = await this.populatePanels(trail, filters, groupNode.values);
356-
children.push(...morePanelsMaybe);
357-
}
358-
359-
return children;
360-
}
361-
362-
private async populatePanels(trail: DataTrail, filters: ReturnType<typeof getFilters>, values: string[]) {
363358
const currentFilterCount = filters?.length || 0;
364359

365-
const previewPanelLayoutItems: SceneFlexItem[] = [];
366-
for (let index = 0; index < values.length; index++) {
367-
const metricName = values[index];
368-
const metric: MetricPanel = this.previewCache[metricName] ?? { name: metricName, index, loaded: false };
369-
const metadata = await trail.getMetricMetadata(metricName);
360+
for (let index = 0; index < metricsList.length; index++) {
361+
const metric = metricsList[index];
362+
const metadata = await trail.getMetricMetadata(metric.name);
370363
const description = getMetricDescription(metadata);
371364

372365
if (this.state.showPreviews) {
373366
if (metric.itemRef && metric.isPanel) {
374-
previewPanelLayoutItems.push(metric.itemRef.resolve());
367+
children.push(metric.itemRef.resolve());
375368
continue;
376369
}
377370
const panel = getPreviewPanelFor(metric.name, index, currentFilterCount, description);
378371

379372
metric.itemRef = panel.getRef();
380373
metric.isPanel = true;
381-
previewPanelLayoutItems.push(panel);
374+
children.push(panel);
382375
} else {
383376
const panel = new SceneCSSGridItem({
384377
$variables: new SceneVariableSet({
@@ -388,11 +381,13 @@ export class MetricSelectScene extends SceneObjectBase<MetricSelectSceneState> i
388381
});
389382
metric.itemRef = panel.getRef();
390383
metric.isPanel = false;
391-
previewPanelLayoutItems.push(panel);
384+
children.push(panel);
392385
}
393386
}
394387

395-
return previewPanelLayoutItems;
388+
const rowTemplate = this.state.showPreviews ? ROW_PREVIEW_HEIGHT : ROW_CARD_HEIGHT;
389+
390+
this.state.body.setState({ children, autoRows: rowTemplate });
396391
}
397392

398393
public updateMetricPanel = (metric: string, isLoaded?: boolean, isEmpty?: boolean) => {
@@ -416,7 +411,7 @@ export class MetricSelectScene extends SceneObjectBase<MetricSelectSceneState> i
416411

417412
public onPrefixFilterChange = (val: SelectableValue) => {
418413
this.setState({ metricPrefix: val.value });
419-
this.buildLayout();
414+
this._refreshMetricNames();
420415
};
421416

422417
public reportPrefixFilterInteraction = (isMenuOpen: boolean) => {

0 commit comments

Comments
 (0)