diff --git a/public/app/core/services/backend_srv.js b/public/app/core/services/backend_srv.js index f27e427c70b..3c96e3c69b4 100644 --- a/public/app/core/services/backend_srv.js +++ b/public/app/core/services/backend_srv.js @@ -7,7 +7,7 @@ define([ function (angular, _, coreModule, config) { 'use strict'; - coreModule.default.service('backendSrv', function($http, alertSrv, $timeout) { + coreModule.default.service('backendSrv', function($http, alertSrv, $timeout, $q) { var self = this; this.get = function(url, params) { @@ -91,8 +91,25 @@ function (angular, _, coreModule, config) { }); }; + var datasourceInFlightRequests = {}; + var HTTP_REQUEST_ABORTED = -1; this.datasourceRequest = function(options) { options.retry = options.retry || 0; + + // A requestID is provided by the datasource as a unique identifier for a + // particular query. If the requestID exists, the promise it is keyed to + // is canceled, canceling the previous datasource request if it is still + // in-flight. + var canceler; + if (options.requestID) { + if (canceler = datasourceInFlightRequests[options.requestID]) { + canceler.resolve(); + } + canceler = $q.defer(); + options.timeout = canceler.promise; + datasourceInFlightRequests[options.requestID] = canceler; + } + var requestIsLocal = options.url.indexOf('/') === 0; var firstAttempt = options.retry === 0; @@ -102,10 +119,21 @@ function (angular, _, coreModule, config) { } return $http(options).then(null, function(err) { + if (err.status === HTTP_REQUEST_ABORTED) { + // TODO: Hitting refresh before the original request returns cancels + // the "loading" animation on the panes, but it should continue to be + // visible. + err.statusText = "request aborted"; + return err; + } + // handle unauthorized for backend requests - if (requestIsLocal && firstAttempt && err.status === 401) { + if (requestIsLocal && firstAttempt && err.status === 401) { return self.loginPing().then(function() { options.retry = 1; + if (canceler) { + canceler.resolve(); + } return self.datasourceRequest(options); }); } diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index d3805d5f44f..4d0fddf58ec 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -182,6 +182,10 @@ class MetricsPanelCtrl extends PanelCtrl { cacheTimeout: this.panel.cacheTimeout }; + metricsQuery.targets.forEach(function(target) { + target.exprID = target.refId + metricsQuery.panelId; + }); + return datasource.query(metricsQuery); } diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index cbd7b1ba24b..3548b4fedb2 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -21,10 +21,11 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS this.withCredentials = instanceSettings.withCredentials; this.lastErrors = {}; - this._request = function(method, url) { + this._request = function(method, url, requestID) { var options: any = { url: this.url + url, - method: method + method: method, + requestID: requestID, }; if (this.basicAuth || this.withCredentials) { @@ -57,6 +58,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS return escapedValues.join('|'); }; + var HTTP_REQUEST_ABORTED = -1; // Called once per panel (graph) this.query = function(options) { var self = this; @@ -75,6 +77,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS var query: any = {}; query.expr = templateSrv.replace(target.expr, options.scopedVars, self.interpolateQueryExpr); + query.requestID = target.exprID; var interval = target.interval || options.interval; var intervalFactor = target.intervalFactor || 1; @@ -105,6 +108,9 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS var result = []; _.each(allResponse, function(response, index) { + if (response.status === HTTP_REQUEST_ABORTED) { + return; + } if (response.status === 'error') { self.lastErrors.query = response.error; throw response.error; @@ -122,7 +128,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS this.performTimeSeriesQuery = function(query, start, end) { var url = '/api/v1/query_range?query=' + encodeURIComponent(query.expr) + '&start=' + start + '&end=' + end + '&step=' + query.step; - return this._request('GET', url); + return this._request('GET', url, query.requestID); }; this.performSuggestQuery = function(query) {