Explore: calculate intervals when building data source request (#19107)

* Explore: calculate intervals when building data source request

* Added unit test

* updated unit test
This commit is contained in:
Torkel Ödegaard 2019-09-16 12:35:39 +02:00 committed by GitHub
parent 7ace80c71c
commit 9d0a076eb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 52 additions and 33 deletions

View File

@ -11,10 +11,11 @@ import {
refreshIntervalToSortOrder,
SortOrder,
sortLogsResult,
buildQueryTransaction,
} from './explore';
import { ExploreUrlState, ExploreMode } from 'app/types/explore';
import store from 'app/core/store';
import { LogsDedupStrategy, LogsModel, LogLevel } from '@grafana/data';
import { LogsDedupStrategy, LogsModel, LogLevel, dateTime } from '@grafana/data';
import { DataQueryError } from '@grafana/ui';
import { liveOption, offOption } from '@grafana/ui/src/components/RefreshPicker/RefreshPicker';
@ -427,4 +428,33 @@ describe('sortLogsResult', () => {
});
});
});
describe('when buildQueryTransaction', () => {
it('it should calculate interval based on time range', () => {
const queries = [{ refId: 'A' }];
const queryOptions = { maxDataPoints: 1000, minInterval: '15s' };
const range = { from: dateTime().subtract(1, 'd'), to: dateTime(), raw: { from: '1h', to: '1h' } };
const transaction = buildQueryTransaction(queries, queryOptions, range, false);
expect(transaction.request.intervalMs).toEqual(60000);
});
it('it should calculate interval taking minInterval into account', () => {
const queries = [{ refId: 'A' }];
const queryOptions = { maxDataPoints: 1000, minInterval: '15s' };
const range = { from: dateTime().subtract(1, 'm'), to: dateTime(), raw: { from: '1h', to: '1h' } };
const transaction = buildQueryTransaction(queries, queryOptions, range, false);
expect(transaction.request.intervalMs).toEqual(15000);
});
it('it should calculate interval taking maxDataPoints into account', () => {
const queries = [{ refId: 'A' }];
const queryOptions = { maxDataPoints: 10, minInterval: '15s' };
const range = { from: dateTime().subtract(1, 'd'), to: dateTime(), raw: { from: '1h', to: '1h' } };
const transaction = buildQueryTransaction(queries, queryOptions, range, false);
expect(transaction.request.interval).toEqual('2h');
});
});
});

View File

@ -13,21 +13,16 @@ import {
LogRowModel,
LogsModel,
LogsDedupStrategy,
IntervalValues,
DefaultTimeZone,
} from '@grafana/data';
import { renderUrl } from 'app/core/utils/url';
import store from 'app/core/store';
import kbn from 'app/core/utils/kbn';
import { getNextRefIdChar } from './query';
// Types
import { DataQuery, DataSourceApi, DataQueryError, DataQueryRequest, PanelModel } from '@grafana/ui';
import {
ExploreUrlState,
HistoryItem,
QueryTransaction,
QueryIntervals,
QueryOptions,
ExploreMode,
} from 'app/types/explore';
import { ExploreUrlState, HistoryItem, QueryTransaction, QueryOptions, ExploreMode } from 'app/types/explore';
import { config } from '../config';
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
@ -103,17 +98,16 @@ export function buildQueryTransaction(
queries: DataQuery[],
queryOptions: QueryOptions,
range: TimeRange,
queryIntervals: QueryIntervals,
scanning: boolean
): QueryTransaction {
const { interval, intervalMs } = queryIntervals;
const configuredQueries = queries.map(query => ({ ...query, ...queryOptions }));
const key = queries.reduce((combinedKey, query) => {
combinedKey += query.key;
return combinedKey;
}, '');
const { interval, intervalMs } = getIntervals(range, queryOptions.minInterval, queryOptions.maxDataPoints);
// Most datasource is using `panelId + query.refId` for cancellation logic.
// Using `format` here because it relates to the view panel that the request is for.
// However, some datasources don't use `panelId + query.refId`, but only `panelId`.
@ -518,3 +512,11 @@ export const stopQueryState = (querySubscription: Unsubscribable) => {
querySubscription.unsubscribe();
}
};
export function getIntervals(range: TimeRange, lowLimit: string, resolution: number): IntervalValues {
if (!resolution) {
return { interval: '1s', intervalMs: 1000 };
}
return kbn.calculateInterval(range, resolution, lowLimit);
}

View File

@ -437,7 +437,6 @@ export function runQueries(exploreId: ExploreId): ThunkResult<void> {
datasourceError,
containerWidth,
isLive: live,
queryIntervals,
range,
scanning,
queryResponse,
@ -461,14 +460,13 @@ export function runQueries(exploreId: ExploreId): ThunkResult<void> {
// Some datasource's query builders allow per-query interval limits,
// but we're using the datasource interval limit for now
const interval = datasourceInstance.interval;
const minInterval = datasourceInstance.interval;
stopQueryState(querySubscription);
const queryOptions = {
interval,
minInterval,
// This is used for logs streaming for buffer size.
// TODO: not sure if this makes sense for normal query when using both graph and table
maxDataPoints: mode === ExploreMode.Logs ? 1000 : containerWidth,
live,
showingGraph,
@ -476,7 +474,7 @@ export function runQueries(exploreId: ExploreId): ThunkResult<void> {
};
const datasourceId = datasourceInstance.meta.id;
const transaction = buildQueryTransaction(queries, queryOptions, range, queryIntervals, scanning);
const transaction = buildQueryTransaction(queries, queryOptions, range, scanning);
let firstResponse = true;

View File

@ -67,9 +67,6 @@ export const DEFAULT_RANGE = {
to: 'now',
};
// Millies step for helper bar charts
const DEFAULT_GRAPH_INTERVAL = 15 * 1000;
export const makeInitialUpdateState = (): ExploreUpdateState => ({
datasource: false,
queries: false,
@ -93,7 +90,6 @@ export const makeExploreItemState = (): ExploreItemState => ({
history: [],
queries: [],
initialized: false,
queryIntervals: { interval: '15s', intervalMs: DEFAULT_GRAPH_INTERVAL },
range: {
from: null,
to: null,
@ -617,7 +613,7 @@ export const processQueryResponse = (
}
const latency = request.endTime ? request.endTime - request.startTime : 0;
const processor = new ResultProcessor(state, series);
const processor = new ResultProcessor(state, series, request.intervalMs);
const graphResult = processor.getGraphResult();
const tableResult = processor.getTableResult();
const logsResult = processor.getLogsResult();

View File

@ -56,7 +56,7 @@ const testContext = (options: any = {}) => {
queryIntervals: { intervalMs: 10 },
} as any) as ExploreItemState;
const resultProcessor = new ResultProcessor(state, combinedOptions.dataFrames);
const resultProcessor = new ResultProcessor(state, combinedOptions.dataFrames, 60000);
return {
dataFrames: combinedOptions.dataFrames,

View File

@ -7,7 +7,7 @@ import { dataFrameToLogsModel } from 'app/core/logs_model';
import { getGraphSeriesModel } from 'app/plugins/panel/graph2/getGraphSeriesModel';
export class ResultProcessor {
constructor(private state: ExploreItemState, private dataFrames: DataFrame[]) {}
constructor(private state: ExploreItemState, private dataFrames: DataFrame[], private intervalMs: number) {}
getGraphResult(): GraphSeriesXY[] {
if (this.state.mode !== ExploreMode.Metrics) {
@ -77,9 +77,7 @@ export class ResultProcessor {
return null;
}
const graphInterval = this.state.queryIntervals.intervalMs;
const newResults = dataFrameToLogsModel(this.dataFrames, graphInterval);
const newResults = dataFrameToLogsModel(this.dataFrames, this.intervalMs);
const sortOrder = refreshIntervalToSortOrder(this.state.refreshInterval);
const sortedNewResults = sortLogsResult(newResults, sortOrder);

View File

@ -186,11 +186,6 @@ export interface ExploreItemState {
*/
logsResult?: LogsModel;
/**
* Query intervals for graph queries to determine how many datapoints to return.
* Needs to be updated when `datasourceInstance` or `containerWidth` is changed.
*/
queryIntervals: QueryIntervals;
/**
* Time range for this Explore. Managed by the time picker and used by all query runs.
*/
@ -330,7 +325,7 @@ export interface QueryIntervals {
}
export interface QueryOptions {
interval: string;
minInterval: string;
maxDataPoints?: number;
live?: boolean;
}