mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prometheus: Add a limit for the series resource api in metrics browser (#91555)
* add a limit for the series resource api in metrics browser * decouple serieslimit from options and only use in metrics browser * add series limit input to metrics browser * add warning * add and fix tests * add new param to jsdoc * do not use the limit in other calls outside metrics browser * update test * trim limit * fix tests, remove limit from non labels calls
This commit is contained in:
parent
de0e6d0fce
commit
f01263803a
@ -106,6 +106,7 @@ export const Components = {
|
||||
metricsBrowser: {
|
||||
openButton: 'data-testid open metrics browser',
|
||||
selectMetric: 'data-testid select a metric',
|
||||
seriesLimit: 'data-testid series limit',
|
||||
metricList: 'data-testid metric list',
|
||||
labelNamesFilter: 'data-testid label names filter',
|
||||
labelValuesFilter: 'data-testid label values filter',
|
||||
|
@ -45,8 +45,12 @@ interface BrowserState {
|
||||
error: string;
|
||||
validationStatus: string;
|
||||
valueSearchTerm: string;
|
||||
seriesLimit?: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_SERIES_LIMIT = '40000';
|
||||
export const REMOVE_SERIES_LIMIT = 'none';
|
||||
|
||||
interface FacettableValue {
|
||||
name: string;
|
||||
selected?: boolean;
|
||||
@ -214,6 +218,10 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
|
||||
this.setState({ metricSearchTerm: event.target.value });
|
||||
};
|
||||
|
||||
onChangeSeriesLimit = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ seriesLimit: event.target.value.trim() });
|
||||
};
|
||||
|
||||
onChangeValueSearch = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ valueSearchTerm: event.target.value });
|
||||
};
|
||||
@ -419,7 +427,7 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
|
||||
this.updateLabelState(lastFacetted, { loading: true }, `Facetting labels for ${selector}`);
|
||||
}
|
||||
try {
|
||||
const possibleLabels = await languageProvider.fetchSeriesLabels(selector, true);
|
||||
const possibleLabels = await languageProvider.fetchSeriesLabels(selector, true, this.state.seriesLimit);
|
||||
// If selector changed, clear loading state and discard result by returning early
|
||||
if (selector !== buildSelector(this.state.labels)) {
|
||||
if (lastFacetted) {
|
||||
@ -492,7 +500,9 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
|
||||
<Stack gap={3}>
|
||||
<div>
|
||||
<div className={styles.section}>
|
||||
<Label description="Once a metric is selected only possible labels are shown.">1. Select a metric</Label>
|
||||
<Label description="Once a metric is selected only possible labels are shown. Labels are limited by the series limit below.">
|
||||
1. Select a metric
|
||||
</Label>
|
||||
<div>
|
||||
<Input
|
||||
onChange={this.onChangeMetricSearch}
|
||||
@ -501,6 +511,17 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.selectMetric}
|
||||
/>
|
||||
</div>
|
||||
<Label description="Set to 'none' to remove limit and show all labels for a selected metric. Removing the limit may cause performance issues.">
|
||||
Series limit
|
||||
</Label>
|
||||
<div>
|
||||
<Input
|
||||
onChange={this.onChangeSeriesLimit}
|
||||
aria-label="Limit results from series endpoint"
|
||||
value={this.state.seriesLimit ?? DEFAULT_SERIES_LIMIT}
|
||||
data-testid={selectors.components.DataSource.Prometheus.queryEditor.code.metricsBrowser.seriesLimit}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
role="list"
|
||||
className={styles.valueListWrapper}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/language_provider.test.ts
|
||||
import { AbstractLabelOperator, dateTime, TimeRange } from '@grafana/data';
|
||||
|
||||
import { DEFAULT_SERIES_LIMIT } from './components/PrometheusMetricsBrowser';
|
||||
import { Label } from './components/monaco-query-field/monaco-completion-provider/situation';
|
||||
import { PrometheusDatasource } from './datasource';
|
||||
import LanguageProvider from './language_provider';
|
||||
@ -301,6 +302,48 @@ describe('Language completion provider', () => {
|
||||
end: toPrometheusTimeString,
|
||||
'match[]': 'interpolated-metric',
|
||||
start: fromPrometheusTimeString,
|
||||
limit: DEFAULT_SERIES_LIMIT,
|
||||
},
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
it("should not use default limit parameter when 'none' is passed to fetchSeriesLabels", () => {
|
||||
const languageProvider = new LanguageProvider({
|
||||
...defaultDatasource,
|
||||
} as PrometheusDatasource);
|
||||
const fetchSeriesLabels = languageProvider.fetchSeriesLabels;
|
||||
const requestSpy = jest.spyOn(languageProvider, 'request');
|
||||
fetchSeriesLabels('metric-with-limit', undefined, 'none');
|
||||
expect(requestSpy).toHaveBeenCalled();
|
||||
expect(requestSpy).toHaveBeenCalledWith(
|
||||
'/api/v1/series',
|
||||
[],
|
||||
{
|
||||
end: toPrometheusTimeString,
|
||||
'match[]': 'metric-with-limit',
|
||||
start: fromPrometheusTimeString,
|
||||
},
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
it("should not have a limit paranter if 'none' is passed to function", () => {
|
||||
const languageProvider = new LanguageProvider({
|
||||
...defaultDatasource,
|
||||
// interpolateString: (string: string) => string.replace(/\$/, 'interpolated-'),
|
||||
} as PrometheusDatasource);
|
||||
const fetchSeriesLabels = languageProvider.fetchSeriesLabels;
|
||||
const requestSpy = jest.spyOn(languageProvider, 'request');
|
||||
fetchSeriesLabels('metric-without-limit', false, 'none');
|
||||
expect(requestSpy).toHaveBeenCalled();
|
||||
expect(requestSpy).toHaveBeenCalledWith(
|
||||
'/api/v1/series',
|
||||
[],
|
||||
{
|
||||
end: toPrometheusTimeString,
|
||||
'match[]': 'metric-without-limit',
|
||||
start: fromPrometheusTimeString,
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
} from '@grafana/data';
|
||||
import { BackendSrvRequest } from '@grafana/runtime';
|
||||
|
||||
import { DEFAULT_SERIES_LIMIT, REMOVE_SERIES_LIMIT } from './components/PrometheusMetricsBrowser';
|
||||
import { Label } from './components/monaco-query-field/monaco-completion-provider/situation';
|
||||
import { PrometheusDatasource } from './datasource';
|
||||
import {
|
||||
@ -30,6 +31,13 @@ const EMPTY_SELECTOR = '{}';
|
||||
// Max number of items (metrics, labels, values) that we display as suggestions. Prevents from running out of memory.
|
||||
export const SUGGESTIONS_LIMIT = 10000;
|
||||
|
||||
type UrlParamsType = {
|
||||
start?: string;
|
||||
end?: string;
|
||||
'match[]'?: string;
|
||||
limit?: string;
|
||||
};
|
||||
|
||||
const buildCacheHeaders = (durationInSeconds: number) => {
|
||||
return {
|
||||
headers: {
|
||||
@ -181,7 +189,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
if (selector === EMPTY_SELECTOR) {
|
||||
return await this.fetchDefaultSeries();
|
||||
} else {
|
||||
return await this.fetchSeriesLabels(selector, withName);
|
||||
return await this.fetchSeriesLabels(selector, withName, REMOVE_SERIES_LIMIT);
|
||||
}
|
||||
} catch (error) {
|
||||
// TODO: better error handling
|
||||
@ -325,7 +333,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
if (this.datasource.hasLabelsMatchAPISupport()) {
|
||||
return this.fetchSeriesLabelsMatch(name, withName);
|
||||
} else {
|
||||
return this.fetchSeriesLabels(name, withName);
|
||||
return this.fetchSeriesLabels(name, withName, REMOVE_SERIES_LIMIT);
|
||||
}
|
||||
};
|
||||
|
||||
@ -334,14 +342,24 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
* they can change over requested time.
|
||||
* @param name
|
||||
* @param withName
|
||||
* @param withLimit
|
||||
*/
|
||||
fetchSeriesLabels = async (name: string, withName?: boolean): Promise<Record<string, string[]>> => {
|
||||
fetchSeriesLabels = async (
|
||||
name: string,
|
||||
withName?: boolean,
|
||||
withLimit?: string
|
||||
): Promise<Record<string, string[]>> => {
|
||||
const interpolatedName = this.datasource.interpolateString(name);
|
||||
const range = this.datasource.getAdjustedInterval(this.timeRange);
|
||||
const urlParams = {
|
||||
let urlParams: UrlParamsType = {
|
||||
...range,
|
||||
'match[]': interpolatedName,
|
||||
};
|
||||
|
||||
if (withLimit !== 'none') {
|
||||
urlParams = { ...urlParams, limit: withLimit ?? DEFAULT_SERIES_LIMIT };
|
||||
}
|
||||
|
||||
const url = `/api/v1/series`;
|
||||
|
||||
const data = await this.request(url, [], urlParams, this.getDefaultCacheHeaders());
|
||||
|
Loading…
Reference in New Issue
Block a user