From 9d0a076eb1159d56224fa75b37d840b8a31a255e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 16 Sep 2019 12:35:39 +0200 Subject: [PATCH] Explore: calculate intervals when building data source request (#19107) * Explore: calculate intervals when building data source request * Added unit test * updated unit test --- public/app/core/utils/explore.test.ts | 32 ++++++++++++++++++- public/app/core/utils/explore.ts | 24 +++++++------- public/app/features/explore/state/actions.ts | 8 ++--- public/app/features/explore/state/reducers.ts | 6 +--- .../explore/utils/ResultProcessor.test.ts | 2 +- .../features/explore/utils/ResultProcessor.ts | 6 ++-- public/app/types/explore.ts | 7 +--- 7 files changed, 52 insertions(+), 33 deletions(-) diff --git a/public/app/core/utils/explore.test.ts b/public/app/core/utils/explore.test.ts index 328bf1b5f80..0f21634675d 100644 --- a/public/app/core/utils/explore.test.ts +++ b/public/app/core/utils/explore.test.ts @@ -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'); + }); + }); }); diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 534c9238db4..38252c7d0e6 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -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); +} diff --git a/public/app/features/explore/state/actions.ts b/public/app/features/explore/state/actions.ts index 46ac0a9331b..cd711839bae 100644 --- a/public/app/features/explore/state/actions.ts +++ b/public/app/features/explore/state/actions.ts @@ -437,7 +437,6 @@ export function runQueries(exploreId: ExploreId): ThunkResult { datasourceError, containerWidth, isLive: live, - queryIntervals, range, scanning, queryResponse, @@ -461,14 +460,13 @@ export function runQueries(exploreId: ExploreId): ThunkResult { // 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 { }; const datasourceId = datasourceInstance.meta.id; - const transaction = buildQueryTransaction(queries, queryOptions, range, queryIntervals, scanning); + const transaction = buildQueryTransaction(queries, queryOptions, range, scanning); let firstResponse = true; diff --git a/public/app/features/explore/state/reducers.ts b/public/app/features/explore/state/reducers.ts index 2874b97b892..27bdd4b6b58 100644 --- a/public/app/features/explore/state/reducers.ts +++ b/public/app/features/explore/state/reducers.ts @@ -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(); diff --git a/public/app/features/explore/utils/ResultProcessor.test.ts b/public/app/features/explore/utils/ResultProcessor.test.ts index 9fa187ea45e..08fcea79c2b 100644 --- a/public/app/features/explore/utils/ResultProcessor.test.ts +++ b/public/app/features/explore/utils/ResultProcessor.test.ts @@ -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, diff --git a/public/app/features/explore/utils/ResultProcessor.ts b/public/app/features/explore/utils/ResultProcessor.ts index 45fe31cb010..453fc540cbf 100644 --- a/public/app/features/explore/utils/ResultProcessor.ts +++ b/public/app/features/explore/utils/ResultProcessor.ts @@ -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); diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index 37427dd6904..09114c24738 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -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; }