Commit aae25c53 by Zoltán Bedi Committed by GitHub

Prometheus: add $__rate_interval variable (#26937)

* Add rate interval variable to prometheus data source

* Add tests + auto complete

* Fix prometheus tests

* Add doc

* Modify test title

* Modify kbn method name after merge
parent f9f60c91
...@@ -110,6 +110,9 @@ Populate a variable with the instances having a certain state over the time rang ...@@ -110,6 +110,9 @@ Populate a variable with the instances having a certain state over the time rang
Query: query_result(max_over_time(<metric>[${__range_s}s]) != <state>) Query: query_result(max_over_time(<metric>[${__range_s}s]) != <state>)
Regex: Regex:
``` ```
### Using `$__rate_interval` variable
The `$__rate_interval` variable is meant to be used in the rate function. It is defined as max( `$__interval` + _Scrape interval_, 4 * _Scrape interval_), where _Scrape interval_ is the Min step setting (AKA query_interval, a setting per PromQL query), if any is set, and otherwise the _Scrape interval_ as set in the Prometheus data source (but ignoring any Min interval setting in the panel, because the latter is modified by the resolution setting).
### Using variables in queries ### Using variables in queries
......
...@@ -1601,9 +1601,28 @@ describe('PrometheusDatasource', () => { ...@@ -1601,9 +1601,28 @@ describe('PrometheusDatasource', () => {
text: expectedRangeSecond * 1000, text: expectedRangeSecond * 1000,
value: expectedRangeSecond * 1000, value: expectedRangeSecond * 1000,
}, },
__rate_interval: {
text: '75s',
value: '75s',
},
}); });
}); });
}); });
describe('The __rate_interval variable', () => {
it('should be 4 times the scrape interval if interval + scrape interval is lower', () => {
const { __rate_interval } = ds.getRateIntervalScopedVariable(23, 23);
expect(__rate_interval.value).toBe('60s');
});
it('should be interval + scrape interval if 4 times the scrape interval is lower', () => {
const { __rate_interval } = ds.getRateIntervalScopedVariable(56, 56);
expect(__rate_interval.value).toBe('71s');
});
it('should fall back to 60s if interval is 0', () => {
const { __rate_interval } = ds.getRateIntervalScopedVariable(0, 0);
expect(__rate_interval.value).toBe('60s');
});
});
}); });
describe('PrometheusDatasource for POST', () => { describe('PrometheusDatasource for POST', () => {
......
...@@ -335,13 +335,18 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -335,13 +335,18 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
const intervalFactor = target.intervalFactor || 1; const intervalFactor = target.intervalFactor || 1;
// Adjust the interval to take into account any specified minimum and interval factor plus Prometheus limits // Adjust the interval to take into account any specified minimum and interval factor plus Prometheus limits
const adjustedInterval = this.adjustInterval(interval, minInterval, range, intervalFactor); const adjustedInterval = this.adjustInterval(interval, minInterval, range, intervalFactor);
let scopedVars = { ...options.scopedVars, ...this.getRangeScopedVars(options.range) }; let scopedVars = {
...options.scopedVars,
...this.getRangeScopedVars(options.range),
...this.getRateIntervalScopedVariable(interval, minInterval),
};
// If the interval was adjusted, make a shallow copy of scopedVars with updated interval vars // If the interval was adjusted, make a shallow copy of scopedVars with updated interval vars
if (interval !== adjustedInterval) { if (interval !== adjustedInterval) {
interval = adjustedInterval; interval = adjustedInterval;
scopedVars = Object.assign({}, options.scopedVars, { scopedVars = Object.assign({}, options.scopedVars, {
__interval: { text: interval + 's', value: interval + 's' }, __interval: { text: interval + 's', value: interval + 's' },
__interval_ms: { text: interval * 1000, value: interval * 1000 }, __interval_ms: { text: interval * 1000, value: interval * 1000 },
...this.getRateIntervalScopedVariable(interval, minInterval),
...this.getRangeScopedVars(options.range), ...this.getRangeScopedVars(options.range),
}); });
} }
...@@ -380,6 +385,16 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -380,6 +385,16 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
return query; return query;
} }
getRateIntervalScopedVariable(interval: number, minInterval: number) {
let intervalInSeconds = minInterval === interval ? kbn.intervalToSeconds(this.interval) : minInterval;
// if intervalInSeconds === 0 then we should fall back to the default 15 seconds
if (intervalInSeconds === 0) {
intervalInSeconds = 15;
}
const rateInterval = Math.max(interval + intervalInSeconds, 4 * intervalInSeconds);
return { __rate_interval: { text: rateInterval + 's', value: rateInterval + 's' } };
}
adjustInterval(interval: number, minInterval: number, range: number, intervalFactor: number) { adjustInterval(interval: number, minInterval: number, range: number, intervalFactor: number) {
// Prometheus will drop queries that might return more than 11000 data points. // Prometheus will drop queries that might return more than 11000 data points.
// Calculate a safe interval as an additional minimum to take into account. // Calculate a safe interval as an additional minimum to take into account.
......
...@@ -124,6 +124,7 @@ describe('Language completion provider', () => { ...@@ -124,6 +124,7 @@ describe('Language completion provider', () => {
{ {
items: [ items: [
{ label: '$__interval', sortText: '$__interval' }, // TODO: figure out why this row and sortText is needed { label: '$__interval', sortText: '$__interval' }, // TODO: figure out why this row and sortText is needed
{ label: '$__rate_interval', sortText: '$__rate_interval' },
{ label: '1m', sortText: '00:01:00' }, { label: '1m', sortText: '00:01:00' },
{ label: '5m', sortText: '00:05:00' }, { label: '5m', sortText: '00:05:00' },
{ label: '10m', sortText: '00:10:00' }, { label: '10m', sortText: '00:10:00' },
......
...@@ -2,6 +2,7 @@ import { CompletionItem } from '@grafana/ui'; ...@@ -2,6 +2,7 @@ import { CompletionItem } from '@grafana/ui';
export const RATE_RANGES: CompletionItem[] = [ export const RATE_RANGES: CompletionItem[] = [
{ label: '$__interval', sortText: '$__interval' }, { label: '$__interval', sortText: '$__interval' },
{ label: '$__rate_interval', sortText: '$__rate_interval' },
{ label: '1m', sortText: '00:01:00' }, { label: '1m', sortText: '00:01:00' },
{ label: '5m', sortText: '00:05:00' }, { label: '5m', sortText: '00:05:00' },
{ label: '10m', sortText: '00:10:00' }, { label: '10m', sortText: '00:10:00' },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment