Prometheus: Remove prometheusResourceBrowserCache feature toggle (#76172)

* Remove prometheusResourceBrowserCache feature toggle

* Remove prometheusResourceBrowserCache feature toggle from prometheus datasource

* Update tests
This commit is contained in:
ismail simsek
2023-10-11 15:18:56 +02:00
committed by GitHub
parent ddb9b64128
commit 14d01e2b6e
11 changed files with 49 additions and 283 deletions
@@ -35,7 +35,6 @@ Some features are enabled by default. You can disable these feature by setting t
| `logsContextDatasourceUi` | Allow datasource to provide custom UI for context view | Yes |
| `gcomOnlyExternalOrgRoleSync` | Prohibits a user from changing organization roles synced with Grafana Cloud auth provider | |
| `prometheusMetricEncyclopedia` | Adds the metrics explorer component to the Prometheus query builder as an option in metric select | Yes |
| `prometheusResourceBrowserCache` | Displays browser caching options in Prometheus data source configuration | Yes |
| `prometheusDataplane` | Changes responses to from Prometheus to be compliant with the dataplane specification. In particular it sets the numeric Field.Name from 'Value' to the value of the `__name__` label when present. | Yes |
| `lokiMetricDataplane` | Changes metric responses from Loki to be compliant with the dataplane specification. | Yes |
| `dataplaneFrontendFallback` | Support dataplane contract field name change for transformations and field name matchers where the name is different | Yes |
@@ -66,7 +66,6 @@ export interface FeatureToggles {
gcomOnlyExternalOrgRoleSync?: boolean;
prometheusMetricEncyclopedia?: boolean;
timeSeriesTable?: boolean;
prometheusResourceBrowserCache?: boolean;
influxdbBackendMigration?: boolean;
clientTokenRotation?: boolean;
prometheusDataplane?: boolean;
-8
View File
@@ -331,14 +331,6 @@ var (
FrontendOnly: true,
Owner: appO11ySquad,
},
{
Name: "prometheusResourceBrowserCache",
Description: "Displays browser caching options in Prometheus data source configuration",
Stage: FeatureStageGeneralAvailability,
FrontendOnly: true,
Expression: "true", // turned on by default
Owner: grafanaObservabilityMetricsSquad,
},
{
Name: "influxdbBackendMigration",
Description: "Query InfluxDB InfluxQL without the proxy",
-1
View File
@@ -47,7 +47,6 @@ individualCookiePreferences,experimental,@grafana/backend-platform,false,false,f
gcomOnlyExternalOrgRoleSync,GA,@grafana/grafana-authnz-team,false,false,false,false
prometheusMetricEncyclopedia,GA,@grafana/observability-metrics,false,false,false,true
timeSeriesTable,experimental,@grafana/app-o11y,false,false,false,true
prometheusResourceBrowserCache,GA,@grafana/observability-metrics,false,false,false,true
influxdbBackendMigration,preview,@grafana/observability-metrics,false,false,false,true
clientTokenRotation,experimental,@grafana/grafana-authnz-team,false,false,false,false
prometheusDataplane,GA,@grafana/observability-metrics,false,false,false,false
1 Name Stage Owner requiresDevMode RequiresLicense RequiresRestart FrontendOnly
47 gcomOnlyExternalOrgRoleSync GA @grafana/grafana-authnz-team false false false false
48 prometheusMetricEncyclopedia GA @grafana/observability-metrics false false false true
49 timeSeriesTable experimental @grafana/app-o11y false false false true
prometheusResourceBrowserCache GA @grafana/observability-metrics false false false true
50 influxdbBackendMigration preview @grafana/observability-metrics false false false true
51 clientTokenRotation experimental @grafana/grafana-authnz-team false false false false
52 prometheusDataplane GA @grafana/observability-metrics false false false false
-4
View File
@@ -199,10 +199,6 @@ const (
// Enable time series table transformer & sparkline cell type
FlagTimeSeriesTable = "timeSeriesTable"
// FlagPrometheusResourceBrowserCache
// Displays browser caching options in Prometheus data source configuration
FlagPrometheusResourceBrowserCache = "prometheusResourceBrowserCache"
// FlagInfluxdbBackendMigration
// Query InfluxDB InfluxQL without the proxy
FlagInfluxdbBackendMigration = "influxdbBackendMigration"
@@ -12,7 +12,6 @@ import { ConfigSubSection } from '@grafana/experimental';
import { getBackendSrv } from '@grafana/runtime/src';
import { InlineField, Input, Select, Switch, useTheme2 } from '@grafana/ui';
import config from '../../../../core/config';
import { useUpdateDatasource } from '../../../../features/datasources/state';
import { PromApplication, PromBuildInfoResponse } from '../../../../types/unified-alerting-dto';
import { QueryEditorMode } from '../querybuilder/shared/types';
@@ -198,7 +197,12 @@ export const PromSettings = (props: Props) => {
spellCheck={false}
placeholder="15s"
onChange={onChangeHandler('timeInterval', options, onOptionsChange)}
onBlur={(e) => updateValidDuration({ ...validDuration, timeInterval: e.currentTarget.value })}
onBlur={(e) =>
updateValidDuration({
...validDuration,
timeInterval: e.currentTarget.value,
})
}
/>
{validateInput(validDuration.timeInterval, DURATION_REGEX, durationError)}
</>
@@ -222,7 +226,12 @@ export const PromSettings = (props: Props) => {
onChange={onChangeHandler('queryTimeout', options, onOptionsChange)}
spellCheck={false}
placeholder="60s"
onBlur={(e) => updateValidDuration({ ...validDuration, queryTimeout: e.currentTarget.value })}
onBlur={(e) =>
updateValidDuration({
...validDuration,
queryTimeout: e.currentTarget.value,
})
}
/>
{validateInput(validDuration.queryTimeout, DURATION_REGEX, durationError)}
</>
@@ -362,33 +371,32 @@ export const PromSettings = (props: Props) => {
</div>
)}
</div>
{config.featureToggles.prometheusResourceBrowserCache && (
<div className="gf-form-inline">
<div className="gf-form max-width-30">
<InlineField
label="Cache level"
labelWidth={PROM_CONFIG_LABEL_WIDTH}
tooltip={
<>
Sets the browser caching level for editor queries. Higher cache settings are recommended for high
cardinality data sources.
</>
<div className="gf-form-inline">
<div className="gf-form max-width-30">
<InlineField
label="Cache level"
labelWidth={PROM_CONFIG_LABEL_WIDTH}
tooltip={
<>
Sets the browser caching level for editor queries. Higher cache settings are recommended for high
cardinality data sources.
</>
}
interactive={true}
disabled={options.readOnly}
>
<Select
width={40}
onChange={onChangeHandler('cacheLevel', options, onOptionsChange)}
options={cacheValueOptions}
value={
cacheValueOptions.find((o) => o.value === options.jsonData.cacheLevel) ?? PrometheusCacheLevel.Low
}
interactive={true}
disabled={options.readOnly}
>
<Select
width={40}
onChange={onChangeHandler('cacheLevel', options, onOptionsChange)}
options={cacheValueOptions}
value={
cacheValueOptions.find((o) => o.value === options.jsonData.cacheLevel) ?? PrometheusCacheLevel.Low
}
/>
</InlineField>
</div>
/>
</InlineField>
</div>
)}
</div>
<div className="gf-form-inline">
<div className="gf-form max-width-30">
@@ -431,7 +439,10 @@ export const PromSettings = (props: Props) => {
<>
<Input
onBlur={(e) =>
updateValidDuration({ ...validDuration, incrementalQueryOverlapWindow: e.currentTarget.value })
updateValidDuration({
...validDuration,
incrementalQueryOverlapWindow: e.currentTarget.value,
})
}
className="width-20"
value={options.jsonData.incrementalQueryOverlapWindow ?? defaultPrometheusQueryOverlapWindow}
@@ -14,14 +14,12 @@ import {
getFieldDisplayName,
LoadingState,
toDataFrame,
VariableHide,
} from '@grafana/data';
import { config } from '@grafana/runtime';
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { QueryOptions } from 'app/types';
import { VariableHide } from '../../../features/variables/types';
import {
alignRange,
extractRuleMappingFromGroups,
@@ -440,50 +438,7 @@ describe('PrometheusDatasource', () => {
});
});
// Remove when prometheusResourceBrowserCache is removed
describe('When prometheusResourceBrowserCache feature flag is off, there should be no change to the query intervals ', () => {
beforeEach(() => {
config.featureToggles.prometheusResourceBrowserCache = false;
});
it('test default 1 minute quantization', () => {
const dataSource = new PrometheusDatasource(
{
...instanceSettings,
jsonData: { ...instanceSettings.jsonData, cacheLevel: PrometheusCacheLevel.Low },
},
templateSrvStub as unknown as TemplateSrv,
timeSrvStub as unknown as TimeSrv
);
const quantizedRange = dataSource.getAdjustedInterval();
const oldRange = dataSource.getTimeRangeParams();
// For "1 minute" the window is unchanged
expect(parseInt(quantizedRange.end, 10) - parseInt(quantizedRange.start, 10)).toBe(60);
expect(parseInt(oldRange.end, 10) - parseInt(oldRange.start, 10)).toBe(60);
});
it('test 10 minute quantization', () => {
const dataSource = new PrometheusDatasource(
{
...instanceSettings,
jsonData: { ...instanceSettings.jsonData, cacheLevel: PrometheusCacheLevel.Medium },
},
templateSrvStub as unknown as TemplateSrv,
timeSrvStub as unknown as TimeSrv
);
const quantizedRange = dataSource.getAdjustedInterval();
const oldRange = dataSource.getTimeRangeParams();
expect(parseInt(quantizedRange.end, 10) - parseInt(quantizedRange.start, 10)).toBe(60);
expect(parseInt(oldRange.end, 10) - parseInt(oldRange.start, 10)).toBe(60);
});
});
describe('Test query range snapping', () => {
beforeEach(() => {
config.featureToggles.prometheusResourceBrowserCache = true;
});
it('test default 1 minute quantization', () => {
const dataSource = new PrometheusDatasource(
{
@@ -46,8 +46,6 @@ import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv';
import { PromApiFeatures, PromApplication } from 'app/types/unified-alerting-dto';
import config from '../../../core/config';
import { addLabelToQuery } from './add_label_to_query';
import { AnnotationQueryEditor } from './components/AnnotationQueryEditor';
import PrometheusLanguageProvider from './language_provider';
@@ -1227,9 +1225,6 @@ export class PrometheusDatasource
* Returns the adjusted "snapped" interval parameters
*/
getAdjustedInterval(): { start: string; end: string } {
if (!config.featureToggles.prometheusResourceBrowserCache) {
return this.getTimeRangeParams();
}
const range = this.timeSrv.timeRange();
return getRangeSnapInterval(this.cacheLevel, range);
}
@@ -2,7 +2,6 @@ import { Editor as SlateEditor } from 'slate';
import Plain from 'slate-plain-serializer';
import { AbstractLabelOperator, dateTime, HistoryItem, TimeRange } from '@grafana/data';
import { config } from '@grafana/runtime';
import { SearchFunctionType } from '@grafana/ui';
import { Label } from './components/monaco-query-field/monaco-completion-provider/situation';
@@ -98,53 +97,7 @@ describe('Language completion provider', () => {
});
});
// @todo clean up prometheusResourceBrowserCache feature flag
describe('getSeriesLabelsDeprecatedLRU', () => {
beforeEach(() => {
config.featureToggles.prometheusResourceBrowserCache = false;
});
it('should call labels endpoint', () => {
const languageProvider = new LanguageProvider({
...defaultDatasource,
hasLabelsMatchAPISupport: () => true,
} as PrometheusDatasource);
const getSeriesLabels = languageProvider.getSeriesLabels;
const requestSpy = jest.spyOn(languageProvider, 'request');
const labelName = 'job';
const labelValue = 'grafana';
getSeriesLabels(`{${labelName}="${labelValue}"}`, [{ name: labelName, value: labelValue, op: '=' }] as Label[]);
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(`/api/v1/labels`, [], {
end: toPrometheusTimeString,
'match[]': '{job="grafana"}',
start: fromPrometheusTimeString,
});
});
it('should call series endpoint', () => {
const languageProvider = new LanguageProvider({
...defaultDatasource,
getAdjustedInterval: () => getRangeSnapInterval(PrometheusCacheLevel.None, getMockQuantizedTimeRangeParams()),
} as PrometheusDatasource);
const getSeriesLabels = languageProvider.getSeriesLabels;
const requestSpy = jest.spyOn(languageProvider, 'request');
const labelName = 'job';
const labelValue = 'grafana';
getSeriesLabels(`{${labelName}="${labelValue}"}`, [{ name: labelName, value: labelValue, op: '=' }] as Label[]);
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith('/api/v1/series', [], {
end: toPrometheusTimeString,
'match[]': '{job="grafana"}',
start: fromPrometheusTimeString,
});
});
});
describe('getSeriesLabels', () => {
beforeEach(() => {
config.featureToggles.prometheusResourceBrowserCache = true;
});
it('should call labels endpoint', () => {
const languageProvider = new LanguageProvider({
...defaultDatasource,
@@ -1,5 +1,4 @@
import { chain, difference, once } from 'lodash';
import { LRUCache } from 'lru-cache';
import Prism from 'prismjs';
import { Value } from 'slate';
@@ -11,7 +10,7 @@ import {
HistoryItem,
LanguageProvider,
} from '@grafana/data';
import { BackendSrvRequest, config } from '@grafana/runtime';
import { BackendSrvRequest } from '@grafana/runtime';
import { CompletionItem, CompletionItemGroup, SearchFunctionType, TypeaheadInput, TypeaheadOutput } from '@grafana/ui';
import { Label } from './components/monaco-query-field/monaco-completion-provider/situation';
@@ -23,7 +22,6 @@ import {
parseSelector,
processHistogramMetrics,
processLabels,
roundSecToMin,
toPromLikeQuery,
} from './language_utils';
import PromqlSyntax, { FUNCTIONS, RATE_RANGES } from './promql';
@@ -116,13 +114,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
datasource: PrometheusDatasource;
labelKeys: string[] = [];
declare labelFetchTs: number;
/**
* Cache for labels of series. This is bit simplistic in the sense that it just counts responses each as a 1 and does
* not account for different size of a response. If that is needed a `length` function can be added in the options.
* 10 as a max size is totally arbitrary right now.
*/
private labelsCache = new LRUCache<string, Record<string, string[]>>({ max: 10 });
private labelValuesCache = new LRUCache<string, string[]>({ max: 10 });
constructor(datasource: PrometheusDatasource, initialValues?: Partial<PromQlLanguageProvider>) {
super();
@@ -135,11 +127,8 @@ export default class PromQlLanguageProvider extends LanguageProvider {
}
getDefaultCacheHeaders() {
// @todo clean up prometheusResourceBrowserCache feature flag
if (config.featureToggles.prometheusResourceBrowserCache) {
if (this.datasource.cacheLevel !== PrometheusCacheLevel.None) {
return buildCacheHeaders(this.datasource.getCacheDurationInMinutes() * 60);
}
if (this.datasource.cacheLevel !== PrometheusCacheLevel.None) {
return buildCacheHeaders(this.datasource.getCacheDurationInMinutes() * 60);
}
return;
}
@@ -177,10 +166,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
};
async loadMetricsMetadata() {
// @todo clean up prometheusResourceBrowserCache feature flag
const headers = config.featureToggles.prometheusResourceBrowserCache
? buildCacheHeaders(this.datasource.getDaysToCacheMetadata() * secondsInDay)
: {};
const headers = buildCacheHeaders(this.datasource.getDaysToCacheMetadata() * secondsInDay);
this.metricsMetadata = fixSummariesMetadata(
await this.request(
'/api/v1/metadata',
@@ -574,11 +560,6 @@ export default class PromQlLanguageProvider extends LanguageProvider {
...(interpolatedMatch && { 'match[]': interpolatedMatch }),
};
// @todo clean up prometheusResourceBrowserCache feature flag
if (!config.featureToggles.prometheusResourceBrowserCache) {
return await this.fetchSeriesValuesLRUCache(interpolatedName, range, name, urlParams);
}
const value = await this.request(
`/api/v1/label/${interpolatedName}/values`,
[],
@@ -588,39 +569,6 @@ export default class PromQlLanguageProvider extends LanguageProvider {
return value ?? [];
};
/**
* @deprecated
* @todo clean up prometheusResourceBrowserCache feature flag
* @param interpolatedName
* @param range
* @param name
* @param urlParams
* @private
*/
private async fetchSeriesValuesLRUCache(
interpolatedName: string | null,
range: { start: string; end: string },
name: string,
urlParams: { start: string; end: string }
) {
const cacheParams = new URLSearchParams({
'match[]': interpolatedName ?? '',
start: roundSecToMin(parseInt(range.start, 10)).toString(),
end: roundSecToMin(parseInt(range.end, 10)).toString(),
name: name,
});
const cacheKey = `/api/v1/label/?${cacheParams.toString()}/values`;
let value: string[] | undefined = this.labelValuesCache.get(cacheKey);
if (!value) {
value = await this.request(`/api/v1/label/${interpolatedName}/values`, [], urlParams);
if (value) {
this.labelValuesCache.set(cacheKey, value);
}
}
return value ?? [];
}
/**
* Gets series labels
* Function to replace old getSeries calls in a way that will provide faster endpoints for new prometheus instances,
@@ -662,53 +610,11 @@ export default class PromQlLanguageProvider extends LanguageProvider {
};
const url = `/api/v1/series`;
if (!config.featureToggles.prometheusResourceBrowserCache) {
return await this.fetchSeriesLabelsLRUCache(interpolatedName, range, withName, url, urlParams);
}
const data = await this.request(url, [], urlParams, this.getDefaultCacheHeaders());
const { values } = processLabels(data, withName);
return values;
};
/**
* @deprecated
* @param interpolatedName
* @param range
* @param withName
* @param url
* @param urlParams
* @private
*/
private async fetchSeriesLabelsLRUCache(
interpolatedName: string,
range: { start: string; end: string },
withName: boolean | undefined,
url: string,
urlParams: { start: string; 'match[]': string; end: string }
) {
// Cache key is a bit different here. We add the `withName` param and also round up to a minute the intervals.
// The rounding may seem strange but makes relative intervals like now-1h less prone to need separate request every
// millisecond while still actually getting all the keys for the correct interval. This still can create problems
// when user does not the newest values for a minute if already cached.
const cacheParams = new URLSearchParams({
'match[]': interpolatedName,
start: roundSecToMin(parseInt(range.start, 10)).toString(),
end: roundSecToMin(parseInt(range.end, 10)).toString(),
withName: withName ? 'true' : 'false',
});
const cacheKey = `/api/v1/series?${cacheParams.toString()}`;
let value = this.labelsCache.get(cacheKey);
if (!value) {
const data = await this.request(url, [], urlParams);
const { values } = processLabels(data, withName);
value = values;
this.labelsCache.set(cacheKey, value);
}
return value;
}
/**
* Fetch labels for a series using /labels endpoint. This is cached by its args but also by the global timeRange currently selected as
* they can change over requested time.
@@ -723,53 +629,12 @@ export default class PromQlLanguageProvider extends LanguageProvider {
'match[]': interpolatedName,
};
const url = `/api/v1/labels`;
if (!config.featureToggles.prometheusResourceBrowserCache) {
return await this.fetchSeriesLabelMatchLRUCache(interpolatedName, range, withName, url, urlParams);
}
const data: string[] = await this.request(url, [], urlParams, this.getDefaultCacheHeaders());
// Convert string array to Record<string , []>
return data.reduce((ac, a) => ({ ...ac, [a]: '' }), {});
};
/**
* @deprecated
* @param interpolatedName
* @param range
* @param withName
* @param url
* @param urlParams
* @private
*/
private async fetchSeriesLabelMatchLRUCache(
interpolatedName: string,
range: { start: string; end: string },
withName: boolean | undefined,
url: string,
urlParams: { start: string; 'match[]': string; end: string }
) {
// Cache key is a bit different here. We add the `withName` param and also round up to a minute the intervals.
// The rounding may seem strange but makes relative intervals like now-1h less prone to need separate request every
// millisecond while still actually getting all the keys for the correct interval. This still can create problems
// when user does not the newest values for a minute if already cached.
const cacheParams = new URLSearchParams({
'match[]': interpolatedName,
start: roundSecToMin(parseInt(range.start, 10)).toString(),
end: roundSecToMin(parseInt(range.end, 10)).toString(),
withName: withName ? 'true' : 'false',
});
const cacheKey = `${url}?${cacheParams.toString()}`;
let value = this.labelsCache.get(cacheKey);
if (!value) {
const data: string[] = await this.request(url, [], urlParams);
// Convert string array to Record<string , []>
value = data.reduce((ac, a) => ({ ...ac, [a]: '' }), {});
this.labelsCache.set(cacheKey, value);
}
return value;
}
/**
* Fetch series for a selector. Use this for raw results. Use fetchSeriesLabels() to get labels.
* @param match
@@ -91,7 +91,9 @@ describe('PrometheusMetricFindQuery', () => {
url: `/api/datasources/uid/ABCDEF/resources/api/v1/labels?start=${raw.from.unix()}&end=${raw.to.unix()}`,
hideFromInspector: true,
showErrorAlert: false,
headers: {},
headers: {
'X-Grafana-Cache': 'private, max-age=60',
},
});
});