mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prometheus: Rework the interaction between auto interval (computed based on graph resolution), min interval (where specified, per query) and intervalFactor (AKA resolution, where specified, per query).
As a bonus, have and reflect the actual interval (not the auto interval), taking into account min interval and Prometheus' 11k data points limit.
This commit is contained in:
@@ -98,14 +98,29 @@ export class PrometheusDatasource {
|
|||||||
activeTargets.push(target);
|
activeTargets.push(target);
|
||||||
|
|
||||||
var query: any = {};
|
var query: any = {};
|
||||||
query.expr = this.templateSrv.replace(target.expr, options.scopedVars, self.interpolateQueryExpr);
|
|
||||||
query.requestId = options.panelId + target.refId;
|
|
||||||
|
|
||||||
var interval = this.templateSrv.replace(target.interval, options.scopedVars) || options.interval;
|
var interval = this.intervalSeconds(options.interval);
|
||||||
|
// Minimum interval ("Min step"), if specified for the query. or same as interval otherwise
|
||||||
|
var minInterval = this.intervalSeconds(this.templateSrv.replace(target.interval, options.scopedVars) || options.interval);
|
||||||
var intervalFactor = target.intervalFactor || 1;
|
var intervalFactor = target.intervalFactor || 1;
|
||||||
target.step = query.step = this.calculateInterval(interval, intervalFactor);
|
|
||||||
var range = Math.ceil(end - start);
|
var range = Math.ceil(end - start);
|
||||||
target.step = query.step = this.adjustStep(query.step, this.intervalSeconds(options.interval), range);
|
// Adjust the interval to take into account any specified minimum plus Prometheus limitations
|
||||||
|
var adjustedInterval = this.adjustInterval(interval, minInterval, range, intervalFactor);
|
||||||
|
|
||||||
|
var scopedVars = options.scopedVars;
|
||||||
|
// If the interval was adjusted, make a shallow copy of scopedVars with updated interval vars
|
||||||
|
if (interval !== adjustedInterval) {
|
||||||
|
interval = adjustedInterval;
|
||||||
|
scopedVars = Object.assign({}, options.scopedVars, {
|
||||||
|
"__interval": {text: interval + "s", value: interval + "s"},
|
||||||
|
"__interval_ms": {text: interval * 1000, value: interval * 1000},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
target.step = query.step = interval * intervalFactor;
|
||||||
|
|
||||||
|
// Only replace vars in expression after having (possibly) updated interval vars
|
||||||
|
query.expr = this.templateSrv.replace(target.expr, scopedVars, self.interpolateQueryExpr);
|
||||||
|
query.requestId = options.panelId + target.refId;
|
||||||
queries.push(query);
|
queries.push(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,13 +155,14 @@ export class PrometheusDatasource {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
adjustStep(step, autoStep, range) {
|
adjustInterval(interval, minInterval, range, intervalFactor) {
|
||||||
// Prometheus drop query if range/step > 11000
|
interval = Math.max(interval, minInterval);
|
||||||
// calibrate step if it is too big
|
// Prometheus will drop queries that might return more than 11000 data points.
|
||||||
if (step !== 0 && range / step > 11000) {
|
// Calibrate interval if it is too small.
|
||||||
step = Math.ceil(range / 11000);
|
if (interval !== 0 && range / intervalFactor / interval > 11000) {
|
||||||
|
interval = Math.ceil(range / intervalFactor / 11000);
|
||||||
}
|
}
|
||||||
return Math.max(step, autoStep);
|
return interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
performTimeSeriesQuery(query, start, end) {
|
performTimeSeriesQuery(query, start, end) {
|
||||||
@@ -206,7 +222,7 @@ export class PrometheusDatasource {
|
|||||||
var end = this.getPrometheusTime(options.range.to, true);
|
var end = this.getPrometheusTime(options.range.to, true);
|
||||||
var query = {
|
var query = {
|
||||||
expr: interpolated,
|
expr: interpolated,
|
||||||
step: this.adjustStep(kbn.interval_to_seconds(step), 0, Math.ceil(end - start)) + 's'
|
step: this.adjustInterval(kbn.interval_to_seconds(step), 0, Math.ceil(end - start), 1) + 's'
|
||||||
};
|
};
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|||||||
@@ -195,4 +195,266 @@ describe('PrometheusDatasource', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('The "step" query parameter', function() {
|
||||||
|
var response = {
|
||||||
|
status: "success",
|
||||||
|
data: {
|
||||||
|
resultType: "matrix",
|
||||||
|
result: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
ctx.$httpBackend.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be min interval when greater than auto interval', function() {
|
||||||
|
var query = {
|
||||||
|
range: { from: moment(1443438674760), to: moment(1443460274760) },
|
||||||
|
targets: [{
|
||||||
|
expr: 'test',
|
||||||
|
interval: '10s'
|
||||||
|
}],
|
||||||
|
interval: '5s'
|
||||||
|
};
|
||||||
|
var urlExpected = 'proxied/api/v1/query_range?query=test' +
|
||||||
|
'&start=1443438675&end=1443460275&step=10';
|
||||||
|
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
|
||||||
|
ctx.ds.query(query);
|
||||||
|
ctx.$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
});
|
||||||
|
it('should be auto interval when greater than min interval', function() {
|
||||||
|
var query = {
|
||||||
|
range: { from: moment(1443438674760), to: moment(1443460274760) },
|
||||||
|
targets: [{
|
||||||
|
expr: 'test',
|
||||||
|
interval: '5s'
|
||||||
|
}],
|
||||||
|
interval: '10s'
|
||||||
|
};
|
||||||
|
var urlExpected = 'proxied/api/v1/query_range?query=test' +
|
||||||
|
'&start=1443438675&end=1443460275&step=10';
|
||||||
|
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
|
||||||
|
ctx.ds.query(query);
|
||||||
|
ctx.$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
});
|
||||||
|
it('should result in querying fewer than 11000 data points', function() {
|
||||||
|
var query = {
|
||||||
|
range: { from: moment(1443438674760), to: moment(1443460274760) },
|
||||||
|
targets: [{ expr: 'test' }],
|
||||||
|
interval: '1s'
|
||||||
|
};
|
||||||
|
var urlExpected = 'proxied/api/v1/query_range?query=test' +
|
||||||
|
'&start=1443438675&end=1443460275&step=2';
|
||||||
|
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
|
||||||
|
ctx.ds.query(query);
|
||||||
|
ctx.$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
});
|
||||||
|
it('should apply intervalFactor to min interval when greater', function() {
|
||||||
|
var query = {
|
||||||
|
range: { from: moment(1443438674760), to: moment(1443460274760) },
|
||||||
|
targets: [{
|
||||||
|
expr: 'test',
|
||||||
|
interval: '10s',
|
||||||
|
intervalFactor: 10
|
||||||
|
}],
|
||||||
|
interval: '5s'
|
||||||
|
};
|
||||||
|
var urlExpected = 'proxied/api/v1/query_range?query=test' +
|
||||||
|
'&start=1443438675&end=1443460275&step=100';
|
||||||
|
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
|
||||||
|
ctx.ds.query(query);
|
||||||
|
ctx.$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
});
|
||||||
|
it('should apply intervalFactor to auto interval when greater', function() {
|
||||||
|
var query = {
|
||||||
|
range: { from: moment(1443438674760), to: moment(1443460274760) },
|
||||||
|
targets: [{
|
||||||
|
expr: 'test',
|
||||||
|
interval: '5s',
|
||||||
|
intervalFactor: 10
|
||||||
|
}],
|
||||||
|
interval: '10s'
|
||||||
|
};
|
||||||
|
var urlExpected = 'proxied/api/v1/query_range?query=test' +
|
||||||
|
'&start=1443438675&end=1443460275&step=100';
|
||||||
|
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
|
||||||
|
ctx.ds.query(query);
|
||||||
|
ctx.$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
});
|
||||||
|
it('should not not be affected by the 11000 data points limit when large enough', function() {
|
||||||
|
var query = {
|
||||||
|
// 1 week range
|
||||||
|
range: { from: moment(1443438674760), to: moment(1444043474760) },
|
||||||
|
targets: [{
|
||||||
|
expr: 'test',
|
||||||
|
intervalFactor: 10
|
||||||
|
}],
|
||||||
|
interval: '10s'
|
||||||
|
};
|
||||||
|
var urlExpected = 'proxied/api/v1/query_range?query=test' +
|
||||||
|
'&start=1443438675&end=1444043475&step=100';
|
||||||
|
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
|
||||||
|
ctx.ds.query(query);
|
||||||
|
ctx.$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
});
|
||||||
|
it('should be determined by the 11000 data points limit when too small', function() {
|
||||||
|
var query = {
|
||||||
|
// 1 week range
|
||||||
|
range: { from: moment(1443438674760), to: moment(1444043474760) },
|
||||||
|
targets: [{
|
||||||
|
expr: 'test',
|
||||||
|
intervalFactor: 10
|
||||||
|
}],
|
||||||
|
interval: '5s'
|
||||||
|
};
|
||||||
|
var urlExpected = 'proxied/api/v1/query_range?query=test' +
|
||||||
|
'&start=1443438675&end=1444043475&step=60';
|
||||||
|
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
|
||||||
|
ctx.ds.query(query);
|
||||||
|
ctx.$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('The __interval and __interval_ms template variables', function() {
|
||||||
|
var response = {
|
||||||
|
status: "success",
|
||||||
|
data: {
|
||||||
|
resultType: "matrix",
|
||||||
|
result: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
ctx.$httpBackend.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be unchanged when auto interval is greater than min interval', function() {
|
||||||
|
var query = {
|
||||||
|
range: { from: moment(1443438674760), to: moment(1443460274760) },
|
||||||
|
targets: [{
|
||||||
|
expr: 'rate(test[$__interval])',
|
||||||
|
interval: '5s'
|
||||||
|
}],
|
||||||
|
interval: '10s',
|
||||||
|
scopedVars: {
|
||||||
|
"__interval": {text: "10s", value: "10s"},
|
||||||
|
"__interval_ms": {text: 10 * 1000, value: 10 * 1000},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var urlExpected = 'proxied/api/v1/query_range?query=' +
|
||||||
|
encodeURIComponent('rate(test[10s])') +
|
||||||
|
'&start=1443438675&end=1443460275&step=10';
|
||||||
|
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
|
||||||
|
ctx.ds.query(query);
|
||||||
|
ctx.$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
|
||||||
|
expect(query.scopedVars.__interval.text).to.be("10s");
|
||||||
|
expect(query.scopedVars.__interval.value).to.be("10s");
|
||||||
|
expect(query.scopedVars.__interval_ms.text).to.be(10 * 1000);
|
||||||
|
expect(query.scopedVars.__interval_ms.value).to.be(10 * 1000);
|
||||||
|
});
|
||||||
|
it('should be min interval when it is greater than auto interval', function() {
|
||||||
|
var query = {
|
||||||
|
range: { from: moment(1443438674760), to: moment(1443460274760) },
|
||||||
|
targets: [{
|
||||||
|
expr: 'rate(test[$__interval])',
|
||||||
|
interval: '10s'
|
||||||
|
}],
|
||||||
|
interval: '5s',
|
||||||
|
scopedVars: {
|
||||||
|
"__interval": {text: "5s", value: "5s"},
|
||||||
|
"__interval_ms": {text: 5 * 1000, value: 5 * 1000},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var urlExpected = 'proxied/api/v1/query_range?query=' +
|
||||||
|
encodeURIComponent('rate(test[10s])') +
|
||||||
|
'&start=1443438675&end=1443460275&step=10';
|
||||||
|
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
|
||||||
|
ctx.ds.query(query);
|
||||||
|
ctx.$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
|
||||||
|
expect(query.scopedVars.__interval.text).to.be("5s");
|
||||||
|
expect(query.scopedVars.__interval.value).to.be("5s");
|
||||||
|
expect(query.scopedVars.__interval_ms.text).to.be(5 * 1000);
|
||||||
|
expect(query.scopedVars.__interval_ms.value).to.be(5 * 1000);
|
||||||
|
});
|
||||||
|
it('should ignore intervalFactor', function() {
|
||||||
|
var query = {
|
||||||
|
range: { from: moment(1443438674760), to: moment(1443460274760) },
|
||||||
|
targets: [{
|
||||||
|
expr: 'rate(test[$__interval])',
|
||||||
|
interval: '5s',
|
||||||
|
intervalFactor: 10
|
||||||
|
}],
|
||||||
|
interval: '10s',
|
||||||
|
scopedVars: {
|
||||||
|
"__interval": {text: "10s", value: "10s"},
|
||||||
|
"__interval_ms": {text: 10 * 1000, value: 10 * 1000},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var urlExpected = 'proxied/api/v1/query_range?query=' +
|
||||||
|
encodeURIComponent('rate(test[10s])') +
|
||||||
|
'&start=1443438675&end=1443460275&step=100';
|
||||||
|
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
|
||||||
|
ctx.ds.query(query);
|
||||||
|
ctx.$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
|
||||||
|
expect(query.scopedVars.__interval.text).to.be("10s");
|
||||||
|
expect(query.scopedVars.__interval.value).to.be("10s");
|
||||||
|
expect(query.scopedVars.__interval_ms.text).to.be(10 * 1000);
|
||||||
|
expect(query.scopedVars.__interval_ms.value).to.be(10 * 1000);
|
||||||
|
});
|
||||||
|
it('should ignore intervalFactor', function() {
|
||||||
|
var query = {
|
||||||
|
range: { from: moment(1443438674760), to: moment(1443460274760) },
|
||||||
|
targets: [{
|
||||||
|
expr: 'rate(test[$__interval])',
|
||||||
|
interval: '10s',
|
||||||
|
intervalFactor: 10
|
||||||
|
}],
|
||||||
|
interval: '5s',
|
||||||
|
scopedVars: {
|
||||||
|
"__interval": {text: "5s", value: "5s"},
|
||||||
|
"__interval_ms": {text: 5 * 1000, value: 5 * 1000},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var urlExpected = 'proxied/api/v1/query_range?query=' +
|
||||||
|
encodeURIComponent('rate(test[10s])') +
|
||||||
|
'&start=1443438675&end=1443460275&step=100';
|
||||||
|
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
|
||||||
|
ctx.ds.query(query);
|
||||||
|
ctx.$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
|
||||||
|
expect(query.scopedVars.__interval.text).to.be("5s");
|
||||||
|
expect(query.scopedVars.__interval.value).to.be("5s");
|
||||||
|
expect(query.scopedVars.__interval_ms.text).to.be(5 * 1000);
|
||||||
|
expect(query.scopedVars.__interval_ms.value).to.be(5 * 1000);
|
||||||
|
});
|
||||||
|
it('should be determined by the 11000 data points limit, accounting for intervalFactor', function() {
|
||||||
|
var query = {
|
||||||
|
// 1 week range
|
||||||
|
range: { from: moment(1443438674760), to: moment(1444043474760) },
|
||||||
|
targets: [{
|
||||||
|
expr: 'rate(test[$__interval])',
|
||||||
|
intervalFactor: 10
|
||||||
|
}],
|
||||||
|
interval: '5s',
|
||||||
|
scopedVars: {
|
||||||
|
"__interval": {text: "5s", value: "5s"},
|
||||||
|
"__interval_ms": {text: 5 * 1000, value: 5 * 1000},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var urlExpected = 'proxied/api/v1/query_range?query=' +
|
||||||
|
encodeURIComponent('rate(test[6s])') +
|
||||||
|
'&start=1443438675&end=1444043475&step=60';
|
||||||
|
ctx.$httpBackend.expect('GET', urlExpected).respond(response);
|
||||||
|
ctx.ds.query(query);
|
||||||
|
ctx.$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
|
||||||
|
expect(query.scopedVars.__interval.text).to.be("5s");
|
||||||
|
expect(query.scopedVars.__interval.value).to.be("5s");
|
||||||
|
expect(query.scopedVars.__interval_ms.text).to.be(5 * 1000);
|
||||||
|
expect(query.scopedVars.__interval_ms.value).to.be(5 * 1000);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user