Skip to content

Commit

Permalink
Explore metrics: Improve performance for build layout (grafana#92238)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
bohandley authored Aug 22, 2024
1 parent 2ad9d8c commit 130a86d
Showing 1 changed file with 45 additions and 50 deletions.
95 changes: 45 additions & 50 deletions public/app/features/trails/MetricSelect/MetricSelectScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
import { Alert, Field, Icon, IconButton, InlineSwitch, Input, Select, Tooltip, useStyles2 } from '@grafana/ui';
import { Trans } from 'app/core/internationalization';

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

// use this to generate groups for metric prefix
const filteredMetricNames = metricNames;

// filter the remaining metrics with the metric prefix
const metricPrefix = this.state.metricPrefix;
if (metricPrefix && metricPrefix !== 'all') {
const prefixRegex = new RegExp(`(^${metricPrefix}.*)`, 'igy');
metricNames = metricNames.filter((metric) => !prefixRegex || prefixRegex.test(metric));
}

const metricNamesWarning = response.limitReached
? `This feature will only return up to ${MAX_METRIC_NAMES} metric names for performance reasons. ` +
`This limit is being exceeded for the current data source. ` +
`Add search terms or label filters to narrow down the number of metric names returned.`
: undefined;

let bodyLayout = this.state.body;
const rootGroupNode = await this.generateGroups(metricNames);

let rootGroupNode = this.state.rootGroup;

// generate groups based on the search metrics input
rootGroupNode = await this.generateGroups(filteredMetricNames);

this.setState({
metricNames,
Expand Down Expand Up @@ -311,74 +324,54 @@ export class MetricSelectScene extends SceneObjectBase<MetricSelectSceneState> i
this.buildLayout();
}

private sortedPreviewMetrics() {
return Object.values(this.previewCache).sort((a, b) => {
if (a.isEmpty && b.isEmpty) {
return a.index - b.index;
}
if (a.isEmpty) {
return 1;
}
if (b.isEmpty) {
return -1;
}
return a.index - b.index;
});
}

private async buildLayout() {
// Temp hack when going back to select metric scene and variable updates
if (this.ignoreNextUpdate) {
this.ignoreNextUpdate = false;
return;
}

if (!this.state.rootGroup) {
const rootGroupNode = await this.generateGroups(this.state.metricNames);
this.setState({ rootGroup: rootGroupNode });
}

const children = await this.populateFilterableViewLayout();
const rowTemplate = this.state.showPreviews ? ROW_PREVIEW_HEIGHT : ROW_CARD_HEIGHT;
this.state.body.setState({ children, autoRows: rowTemplate });
}
const children: SceneFlexItem[] = [];

private async populateFilterableViewLayout() {
const trail = getTrailFor(this);

const metricsList = this.sortedPreviewMetrics();

// Get the current filters to determine the count of them
// Which is required for `getPreviewPanelFor`
const filters = getFilters(this);

let rootGroupNode = this.state.rootGroup;
if (!rootGroupNode) {
rootGroupNode = await this.generateGroups(this.state.metricNames);
this.setState({ rootGroup: rootGroupNode });
}

const children: SceneFlexItem[] = [];

for (const [groupKey, groupNode] of rootGroupNode.groups) {
if (this.state.metricPrefix !== METRIC_PREFIX_ALL && this.state.metricPrefix !== groupKey) {
continue;
}

for (const [_, value] of groupNode.groups) {
const panels = await this.populatePanels(trail, filters, value.values);
children.push(...panels);
}

const morePanelsMaybe = await this.populatePanels(trail, filters, groupNode.values);
children.push(...morePanelsMaybe);
}

return children;
}

private async populatePanels(trail: DataTrail, filters: ReturnType<typeof getFilters>, values: string[]) {
const currentFilterCount = filters?.length || 0;

const previewPanelLayoutItems: SceneFlexItem[] = [];
for (let index = 0; index < values.length; index++) {
const metricName = values[index];
const metric: MetricPanel = this.previewCache[metricName] ?? { name: metricName, index, loaded: false };
const metadata = await trail.getMetricMetadata(metricName);
for (let index = 0; index < metricsList.length; index++) {
const metric = metricsList[index];
const metadata = await trail.getMetricMetadata(metric.name);
const description = getMetricDescription(metadata);

if (this.state.showPreviews) {
if (metric.itemRef && metric.isPanel) {
previewPanelLayoutItems.push(metric.itemRef.resolve());
children.push(metric.itemRef.resolve());
continue;
}
const panel = getPreviewPanelFor(metric.name, index, currentFilterCount, description);

metric.itemRef = panel.getRef();
metric.isPanel = true;
previewPanelLayoutItems.push(panel);
children.push(panel);
} else {
const panel = new SceneCSSGridItem({
$variables: new SceneVariableSet({
Expand All @@ -388,11 +381,13 @@ export class MetricSelectScene extends SceneObjectBase<MetricSelectSceneState> i
});
metric.itemRef = panel.getRef();
metric.isPanel = false;
previewPanelLayoutItems.push(panel);
children.push(panel);
}
}

return previewPanelLayoutItems;
const rowTemplate = this.state.showPreviews ? ROW_PREVIEW_HEIGHT : ROW_CARD_HEIGHT;

this.state.body.setState({ children, autoRows: rowTemplate });
}

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

public onPrefixFilterChange = (val: SelectableValue) => {
this.setState({ metricPrefix: val.value });
this.buildLayout();
this._refreshMetricNames();
};

public reportPrefixFilterInteraction = (isMenuOpen: boolean) => {
Expand Down

0 comments on commit 130a86d

Please sign in to comment.