The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/plugins/datasource/loki/querySplitting.test.ts

347 lines
14 KiB

Loki Query Splitting: Split queries into sub-queries with smaller time interval (#62767) * Range splitting: range splitting function * Range splitting: experiment with 1 hour splits * Range splitting: reorganize code * Range splitting: improve code readability and meaning * Range splitting: add partition limit to prevent infinite loops * Range splitting: add error handling * Range splitting: disable for logs queries * Range splitting: support any arbitrary time splitting + respect original from/to in the partition * Chore: remove console logs * Chore: delete unused import * Range splitting: actually send requests in sequence * Range splitting: do not split when > 1 query * Range splitting: combine frames * Chore: rename function * split in reverse * polished reversing * keep reference to the right frame in the result * Range splitting: change request state to Streaming * Range splitting: fix moving only 1 unit of time instead of the provided one * Chore: change default parameter to timeShift = 1 * Range splitting: do not split for range queqries * Range splitting: add initial support for log queries * Range splitting: do not use MutableDataFrame It has bad performance and it's not required * Chore: remove unused export * Query Splitting: move to module * loki: split: fix off-by-one error (#62966) loki: split: fix off-by-one loop * Range splitting: disable for logs volume queries * Range splitting: combine any number of fields, not just hardcoded 2 * Range splitting: optimize frame-combining function * Range splitting: further optimize * Range splitting: combine frame length * Range splitting: combine stats * Range splitting: combine stats without assuming the same order * Query splitting: catch and raise errors * Range splitting: create feature flag * Range splitting: implement feature flag * Range splitting: add unit test for datasource query * Range splitting: add basic test for runPartitionedQuery * Range splitting: add unit test for resultLimitReached * Range splitting: test frame merging * Chore: fix unit test --------- Co-authored-by: Sven Grossmann <svennergr@gmail.com> Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>
2 years ago
import { of } from 'rxjs';
import { getQueryOptions } from 'test/helpers/getQueryOptions';
import { dateTime } from '@grafana/data';
import { LoadingState } from '@grafana/schema';
Loki Query Splitting: Split queries into sub-queries with smaller time interval (#62767) * Range splitting: range splitting function * Range splitting: experiment with 1 hour splits * Range splitting: reorganize code * Range splitting: improve code readability and meaning * Range splitting: add partition limit to prevent infinite loops * Range splitting: add error handling * Range splitting: disable for logs queries * Range splitting: support any arbitrary time splitting + respect original from/to in the partition * Chore: remove console logs * Chore: delete unused import * Range splitting: actually send requests in sequence * Range splitting: do not split when > 1 query * Range splitting: combine frames * Chore: rename function * split in reverse * polished reversing * keep reference to the right frame in the result * Range splitting: change request state to Streaming * Range splitting: fix moving only 1 unit of time instead of the provided one * Chore: change default parameter to timeShift = 1 * Range splitting: do not split for range queqries * Range splitting: add initial support for log queries * Range splitting: do not use MutableDataFrame It has bad performance and it's not required * Chore: remove unused export * Query Splitting: move to module * loki: split: fix off-by-one error (#62966) loki: split: fix off-by-one loop * Range splitting: disable for logs volume queries * Range splitting: combine any number of fields, not just hardcoded 2 * Range splitting: optimize frame-combining function * Range splitting: further optimize * Range splitting: combine frame length * Range splitting: combine stats * Range splitting: combine stats without assuming the same order * Query splitting: catch and raise errors * Range splitting: create feature flag * Range splitting: implement feature flag * Range splitting: add unit test for datasource query * Range splitting: add basic test for runPartitionedQuery * Range splitting: add unit test for resultLimitReached * Range splitting: test frame merging * Chore: fix unit test --------- Co-authored-by: Sven Grossmann <svennergr@gmail.com> Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>
2 years ago
import { LokiDatasource } from './datasource';
import * as logsTimeSplit from './logsTimeSplitting';
import * as metricTimeSplit from './metricTimeSplitting';
import { createLokiDatasource, getMockFrames } from './mocks';
import { runSplitQuery } from './querySplitting';
import { LokiQuery, LokiQueryType } from './types';
Loki Query Splitting: Split queries into sub-queries with smaller time interval (#62767) * Range splitting: range splitting function * Range splitting: experiment with 1 hour splits * Range splitting: reorganize code * Range splitting: improve code readability and meaning * Range splitting: add partition limit to prevent infinite loops * Range splitting: add error handling * Range splitting: disable for logs queries * Range splitting: support any arbitrary time splitting + respect original from/to in the partition * Chore: remove console logs * Chore: delete unused import * Range splitting: actually send requests in sequence * Range splitting: do not split when > 1 query * Range splitting: combine frames * Chore: rename function * split in reverse * polished reversing * keep reference to the right frame in the result * Range splitting: change request state to Streaming * Range splitting: fix moving only 1 unit of time instead of the provided one * Chore: change default parameter to timeShift = 1 * Range splitting: do not split for range queqries * Range splitting: add initial support for log queries * Range splitting: do not use MutableDataFrame It has bad performance and it's not required * Chore: remove unused export * Query Splitting: move to module * loki: split: fix off-by-one error (#62966) loki: split: fix off-by-one loop * Range splitting: disable for logs volume queries * Range splitting: combine any number of fields, not just hardcoded 2 * Range splitting: optimize frame-combining function * Range splitting: further optimize * Range splitting: combine frame length * Range splitting: combine stats * Range splitting: combine stats without assuming the same order * Query splitting: catch and raise errors * Range splitting: create feature flag * Range splitting: implement feature flag * Range splitting: add unit test for datasource query * Range splitting: add basic test for runPartitionedQuery * Range splitting: add unit test for resultLimitReached * Range splitting: test frame merging * Chore: fix unit test --------- Co-authored-by: Sven Grossmann <svennergr@gmail.com> Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>
2 years ago
describe('runSplitQuery()', () => {
Loki Query Splitting: Split queries into sub-queries with smaller time interval (#62767) * Range splitting: range splitting function * Range splitting: experiment with 1 hour splits * Range splitting: reorganize code * Range splitting: improve code readability and meaning * Range splitting: add partition limit to prevent infinite loops * Range splitting: add error handling * Range splitting: disable for logs queries * Range splitting: support any arbitrary time splitting + respect original from/to in the partition * Chore: remove console logs * Chore: delete unused import * Range splitting: actually send requests in sequence * Range splitting: do not split when > 1 query * Range splitting: combine frames * Chore: rename function * split in reverse * polished reversing * keep reference to the right frame in the result * Range splitting: change request state to Streaming * Range splitting: fix moving only 1 unit of time instead of the provided one * Chore: change default parameter to timeShift = 1 * Range splitting: do not split for range queqries * Range splitting: add initial support for log queries * Range splitting: do not use MutableDataFrame It has bad performance and it's not required * Chore: remove unused export * Query Splitting: move to module * loki: split: fix off-by-one error (#62966) loki: split: fix off-by-one loop * Range splitting: disable for logs volume queries * Range splitting: combine any number of fields, not just hardcoded 2 * Range splitting: optimize frame-combining function * Range splitting: further optimize * Range splitting: combine frame length * Range splitting: combine stats * Range splitting: combine stats without assuming the same order * Query splitting: catch and raise errors * Range splitting: create feature flag * Range splitting: implement feature flag * Range splitting: add unit test for datasource query * Range splitting: add basic test for runPartitionedQuery * Range splitting: add unit test for resultLimitReached * Range splitting: test frame merging * Chore: fix unit test --------- Co-authored-by: Sven Grossmann <svennergr@gmail.com> Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>
2 years ago
let datasource: LokiDatasource;
const range = {
from: dateTime('2023-02-08T05:00:00.000Z'),
to: dateTime('2023-02-10T06:00:00.000Z'),
raw: {
Loki Query Splitting: Split queries into sub-queries with smaller time interval (#62767) * Range splitting: range splitting function * Range splitting: experiment with 1 hour splits * Range splitting: reorganize code * Range splitting: improve code readability and meaning * Range splitting: add partition limit to prevent infinite loops * Range splitting: add error handling * Range splitting: disable for logs queries * Range splitting: support any arbitrary time splitting + respect original from/to in the partition * Chore: remove console logs * Chore: delete unused import * Range splitting: actually send requests in sequence * Range splitting: do not split when > 1 query * Range splitting: combine frames * Chore: rename function * split in reverse * polished reversing * keep reference to the right frame in the result * Range splitting: change request state to Streaming * Range splitting: fix moving only 1 unit of time instead of the provided one * Chore: change default parameter to timeShift = 1 * Range splitting: do not split for range queqries * Range splitting: add initial support for log queries * Range splitting: do not use MutableDataFrame It has bad performance and it's not required * Chore: remove unused export * Query Splitting: move to module * loki: split: fix off-by-one error (#62966) loki: split: fix off-by-one loop * Range splitting: disable for logs volume queries * Range splitting: combine any number of fields, not just hardcoded 2 * Range splitting: optimize frame-combining function * Range splitting: further optimize * Range splitting: combine frame length * Range splitting: combine stats * Range splitting: combine stats without assuming the same order * Query splitting: catch and raise errors * Range splitting: create feature flag * Range splitting: implement feature flag * Range splitting: add unit test for datasource query * Range splitting: add basic test for runPartitionedQuery * Range splitting: add unit test for resultLimitReached * Range splitting: test frame merging * Chore: fix unit test --------- Co-authored-by: Sven Grossmann <svennergr@gmail.com> Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>
2 years ago
from: dateTime('2023-02-08T05:00:00.000Z'),
to: dateTime('2023-02-10T06:00:00.000Z'),
},
};
const request = getQueryOptions<LokiQuery>({
targets: [{ expr: 'count_over_time({a="b"}[1m])', refId: 'A' }],
range,
Loki Query Splitting: Split queries into sub-queries with smaller time interval (#62767) * Range splitting: range splitting function * Range splitting: experiment with 1 hour splits * Range splitting: reorganize code * Range splitting: improve code readability and meaning * Range splitting: add partition limit to prevent infinite loops * Range splitting: add error handling * Range splitting: disable for logs queries * Range splitting: support any arbitrary time splitting + respect original from/to in the partition * Chore: remove console logs * Chore: delete unused import * Range splitting: actually send requests in sequence * Range splitting: do not split when > 1 query * Range splitting: combine frames * Chore: rename function * split in reverse * polished reversing * keep reference to the right frame in the result * Range splitting: change request state to Streaming * Range splitting: fix moving only 1 unit of time instead of the provided one * Chore: change default parameter to timeShift = 1 * Range splitting: do not split for range queqries * Range splitting: add initial support for log queries * Range splitting: do not use MutableDataFrame It has bad performance and it's not required * Chore: remove unused export * Query Splitting: move to module * loki: split: fix off-by-one error (#62966) loki: split: fix off-by-one loop * Range splitting: disable for logs volume queries * Range splitting: combine any number of fields, not just hardcoded 2 * Range splitting: optimize frame-combining function * Range splitting: further optimize * Range splitting: combine frame length * Range splitting: combine stats * Range splitting: combine stats without assuming the same order * Query splitting: catch and raise errors * Range splitting: create feature flag * Range splitting: implement feature flag * Range splitting: add unit test for datasource query * Range splitting: add basic test for runPartitionedQuery * Range splitting: add unit test for resultLimitReached * Range splitting: test frame merging * Chore: fix unit test --------- Co-authored-by: Sven Grossmann <svennergr@gmail.com> Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>
2 years ago
});
beforeEach(() => {
datasource = createLokiDatasource();
jest.spyOn(datasource, 'runQuery').mockReturnValue(of({ data: [] }));
});
test('Splits datasource queries', async () => {
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
Loki Query Splitting: Split queries into sub-queries with smaller time interval (#62767) * Range splitting: range splitting function * Range splitting: experiment with 1 hour splits * Range splitting: reorganize code * Range splitting: improve code readability and meaning * Range splitting: add partition limit to prevent infinite loops * Range splitting: add error handling * Range splitting: disable for logs queries * Range splitting: support any arbitrary time splitting + respect original from/to in the partition * Chore: remove console logs * Chore: delete unused import * Range splitting: actually send requests in sequence * Range splitting: do not split when > 1 query * Range splitting: combine frames * Chore: rename function * split in reverse * polished reversing * keep reference to the right frame in the result * Range splitting: change request state to Streaming * Range splitting: fix moving only 1 unit of time instead of the provided one * Chore: change default parameter to timeShift = 1 * Range splitting: do not split for range queqries * Range splitting: add initial support for log queries * Range splitting: do not use MutableDataFrame It has bad performance and it's not required * Chore: remove unused export * Query Splitting: move to module * loki: split: fix off-by-one error (#62966) loki: split: fix off-by-one loop * Range splitting: disable for logs volume queries * Range splitting: combine any number of fields, not just hardcoded 2 * Range splitting: optimize frame-combining function * Range splitting: further optimize * Range splitting: combine frame length * Range splitting: combine stats * Range splitting: combine stats without assuming the same order * Query splitting: catch and raise errors * Range splitting: create feature flag * Range splitting: implement feature flag * Range splitting: add unit test for datasource query * Range splitting: add basic test for runPartitionedQuery * Range splitting: add unit test for resultLimitReached * Range splitting: test frame merging * Chore: fix unit test --------- Co-authored-by: Sven Grossmann <svennergr@gmail.com> Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>
2 years ago
// 3 days, 3 chunks, 3 requests.
expect(datasource.runQuery).toHaveBeenCalledTimes(3);
});
});
test('Handles and reports rerrors', async () => {
jest
.spyOn(datasource, 'runQuery')
.mockReturnValue(of({ state: LoadingState.Error, error: { refId: 'A', message: 'Error' }, data: [] }));
await expect(runSplitQuery(datasource, request)).toEmitValuesWith((values) => {
expect(values).toEqual([{ error: { refId: 'A', message: 'Error' }, data: [], state: LoadingState.Streaming }]);
});
});
describe('Hidden queries', () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: 'count_over_time({a="b"}[1m])', refId: 'A', hide: true },
{ expr: '{a="b"}', refId: 'B' },
],
range,
});
beforeAll(() => {
jest.spyOn(logsTimeSplit, 'splitTimeRange').mockReturnValue([]);
jest.spyOn(metricTimeSplit, 'splitTimeRange').mockReturnValue([]);
});
afterAll(() => {
jest.mocked(logsTimeSplit.splitTimeRange).mockRestore();
jest.mocked(metricTimeSplit.splitTimeRange).mockRestore();
});
test('Ignores hidden queries', async () => {
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
expect(logsTimeSplit.splitTimeRange).toHaveBeenCalled();
expect(metricTimeSplit.splitTimeRange).not.toHaveBeenCalled();
});
});
});
describe('Dynamic maxLines for logs requests', () => {
const request = getQueryOptions<LokiQuery>({
targets: [{ expr: '{a="b"}', refId: 'A', maxLines: 4 }],
range,
});
const { logFrameA } = getMockFrames();
beforeEach(() => {
jest.spyOn(datasource, 'runQuery').mockReturnValue(of({ data: [logFrameA], refId: 'A' }));
});
test('Stops requesting once maxLines of logs have been received', async () => {
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// 3 days, 3 chunks, 2 responses of 2 logs, 2 requests
expect(datasource.runQuery).toHaveBeenCalledTimes(2);
});
});
test('Performs all the requests if maxLines has not been reached', async () => {
request.targets[0].maxLines = 9999;
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// 3 days, 3 chunks, 3 responses of 2 logs, 3 requests
expect(datasource.runQuery).toHaveBeenCalledTimes(3);
});
});
test('Performs all the requests if not a log query', async () => {
request.targets[0].maxLines = 1;
request.targets[0].expr = 'count_over_time({a="b"}[1m])';
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// 3 days, 3 chunks, 3 responses of 2 logs, 3 requests
expect(datasource.runQuery).toHaveBeenCalledTimes(3);
});
});
});
describe('Splitting multiple targets', () => {
beforeEach(() => {
jest.spyOn(datasource, 'runQuery').mockReturnValue(of({ data: [], refId: 'A' }));
});
test('Sends logs and metric queries individually', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: '{a="b"}', refId: 'A' },
{ expr: 'count_over_time({a="b"}[1m])', refId: 'B' },
],
range,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// 3 days, 3 chunks, 1x Metric + 1x Log, 6 requests.
expect(datasource.runQuery).toHaveBeenCalledTimes(6);
});
});
test('Groups metric queries', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: 'count_over_time({a="b"}[1m])', refId: 'A' },
{ expr: 'count_over_time({c="d"}[1m])', refId: 'B' },
],
range,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// 3 days, 3 chunks, 1x2 Metric, 3 requests.
expect(datasource.runQuery).toHaveBeenCalledTimes(3);
});
});
test('Groups logs queries', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: '{a="b"}', refId: 'A' },
{ expr: '{c="d"}', refId: 'B' },
],
range,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// 3 days, 3 chunks, 1x2 Logs, 3 requests.
expect(datasource.runQuery).toHaveBeenCalledTimes(3);
});
});
test('Groups instant queries', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: 'count_over_time({a="b"}[1m])', refId: 'A', queryType: LokiQueryType.Instant },
{ expr: 'count_over_time({c="d"}[1m])', refId: 'B', queryType: LokiQueryType.Instant },
],
range,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// Instant queries are omitted from splitting
expect(datasource.runQuery).toHaveBeenCalledTimes(1);
});
});
test('Respects maxLines of logs queries', async () => {
const { logFrameA } = getMockFrames();
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: '{a="b"}', refId: 'A', maxLines: logFrameA.fields[0].values.length },
{ expr: 'count_over_time({a="b"}[1m])', refId: 'B' },
],
range,
});
jest.spyOn(datasource, 'runQuery').mockReturnValue(of({ data: [], refId: 'B' }));
jest.spyOn(datasource, 'runQuery').mockReturnValueOnce(of({ data: [logFrameA], refId: 'A' }));
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// 3 days, 3 chunks, 1x Logs + 3x Metric, 3 requests.
expect(datasource.runQuery).toHaveBeenCalledTimes(4);
});
});
test('Groups multiple queries into logs, queries, and instant', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: 'count_over_time({a="b"}[1m])', refId: 'A', queryType: LokiQueryType.Instant },
{ expr: '{c="d"}', refId: 'B' },
{ expr: 'count_over_time({c="d"}[1m])', refId: 'C' },
],
range,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// 3 days, 3 chunks, 3x Logs + 3x Metric + 1x Instant, 7 requests.
expect(datasource.runQuery).toHaveBeenCalledTimes(7);
});
});
});
describe('Splitting targets based on splitDuration', () => {
const range1h = {
from: dateTime('2023-02-08T05:00:00.000Z'),
to: dateTime('2023-02-08T06:00:00.000Z'),
raw: {
from: dateTime('2023-02-08T05:00:00.000Z'),
to: dateTime('2023-02-08T06:00:00.000Z'),
},
};
beforeEach(() => {
jest.spyOn(datasource, 'runQuery').mockReturnValue(of({ data: [], refId: 'A' }));
});
test('with 30m splitDuration runs 2 queries', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [{ expr: '{a="b"}', refId: 'A', splitDuration: '30m' }],
range: range1h,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
expect(datasource.runQuery).toHaveBeenCalledTimes(2);
});
});
test('with 1h splitDuration runs 1 queries', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [{ expr: '{a="b"}', refId: 'A', splitDuration: '1h' }],
range: range1h,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
expect(datasource.runQuery).toHaveBeenCalledTimes(1);
});
});
test('with 1h splitDuration and 2 targets runs 1 queries', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: '{a="b"}', refId: 'A', splitDuration: '1h' },
{ expr: '{a="b"}', refId: 'B', splitDuration: '1h' },
],
range: range1h,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
expect(datasource.runQuery).toHaveBeenCalledTimes(1);
});
});
test('with 1h/30m splitDuration and 2 targets runs 3 queries', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: '{a="b"}', refId: 'A', splitDuration: '1h' },
{ expr: '{a="b"}', refId: 'B', splitDuration: '30m' },
],
range: range1h,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// 2 x 30m + 1 x 1h
expect(datasource.runQuery).toHaveBeenCalledTimes(3);
});
});
test('with mixed splitDuration runs the expected amount of queries', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: 'count_over_time({c="d"}[1m])', refId: 'A', splitDuration: '15m' },
{ expr: '{a="b"}', refId: 'B', splitDuration: '15m' },
{ expr: '{a="b"}', refId: 'C', splitDuration: '1h' },
],
range: range1h,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// 4 * 15m + 4 * 15m + 1 * 1h
expect(datasource.runQuery).toHaveBeenCalledTimes(9);
});
});
test('with 1h/30m splitDuration and 1 log and 2 metric target runs 3 queries', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: '{a="b"}', refId: 'A', splitDuration: '1h' },
{ expr: 'count_over_time({c="d"}[1m])', refId: 'C', splitDuration: '30m' },
],
range: range1h,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// 2 x 30m + 1 x 1h
expect(datasource.runQuery).toHaveBeenCalledTimes(3);
});
});
});
describe('Splitting targets based on resolution', () => {
const range1d = {
from: dateTime('2023-02-08T05:00:00.000Z'),
to: dateTime('2023-02-09T05:00:00.000Z'),
raw: {
from: dateTime('2023-02-08T05:00:00.000Z'),
to: dateTime('2023-02-09T05:00:00.000Z'),
},
};
test('Groups logs queries by resolution', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: '{a="b"}', refId: 'A', resolution: 3 },
{ expr: '{a="b"}', refId: 'B', resolution: 5 },
],
range: range1d,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// A, B
expect(datasource.runQuery).toHaveBeenCalledTimes(2);
});
});
test('Groups metric queries by resolution', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: 'count_over_time({a="b"}[1m])', refId: 'A', resolution: 3 },
{ expr: 'count_over_time{a="b"}[1m])', refId: 'B', resolution: 5 },
],
range: range1d,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// A, B
expect(datasource.runQuery).toHaveBeenCalledTimes(2);
});
});
test('Groups mixed queries by resolution', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: '{a="b"}', refId: 'A', resolution: 3 },
{ expr: '{a="b"}', refId: 'B', resolution: 5 },
{ expr: 'count_over_time({a="b"}[1m])', refId: 'C', resolution: 3 },
{ expr: 'count_over_time{a="b"}[1m])', refId: 'D', resolution: 5 },
{ expr: '{a="b"}', refId: 'E', resolution: 5, queryType: LokiQueryType.Instant },
],
range: range1d,
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// A, B, C, D, E
expect(datasource.runQuery).toHaveBeenCalledTimes(5);
});
});
test('Chunked groups mixed queries by resolution', async () => {
const request = getQueryOptions<LokiQuery>({
targets: [
{ expr: '{a="b"}', refId: 'A', resolution: 3 },
{ expr: '{a="b"}', refId: 'B', resolution: 5 },
{ expr: 'count_over_time({a="b"}[1m])', refId: 'C', resolution: 3 },
{ expr: 'count_over_time{a="b"}[1m])', refId: 'D', resolution: 5 },
{ expr: '{a="b"}', refId: 'E', resolution: 5, queryType: LokiQueryType.Instant },
],
range, // 3 days
});
await expect(runSplitQuery(datasource, request)).toEmitValuesWith(() => {
// 3 * A, 3 * B, 3 * C, 3 * D, 1 * E
expect(datasource.runQuery).toHaveBeenCalledTimes(13);
});
});
});
Loki Query Splitting: Split queries into sub-queries with smaller time interval (#62767) * Range splitting: range splitting function * Range splitting: experiment with 1 hour splits * Range splitting: reorganize code * Range splitting: improve code readability and meaning * Range splitting: add partition limit to prevent infinite loops * Range splitting: add error handling * Range splitting: disable for logs queries * Range splitting: support any arbitrary time splitting + respect original from/to in the partition * Chore: remove console logs * Chore: delete unused import * Range splitting: actually send requests in sequence * Range splitting: do not split when > 1 query * Range splitting: combine frames * Chore: rename function * split in reverse * polished reversing * keep reference to the right frame in the result * Range splitting: change request state to Streaming * Range splitting: fix moving only 1 unit of time instead of the provided one * Chore: change default parameter to timeShift = 1 * Range splitting: do not split for range queqries * Range splitting: add initial support for log queries * Range splitting: do not use MutableDataFrame It has bad performance and it's not required * Chore: remove unused export * Query Splitting: move to module * loki: split: fix off-by-one error (#62966) loki: split: fix off-by-one loop * Range splitting: disable for logs volume queries * Range splitting: combine any number of fields, not just hardcoded 2 * Range splitting: optimize frame-combining function * Range splitting: further optimize * Range splitting: combine frame length * Range splitting: combine stats * Range splitting: combine stats without assuming the same order * Query splitting: catch and raise errors * Range splitting: create feature flag * Range splitting: implement feature flag * Range splitting: add unit test for datasource query * Range splitting: add basic test for runPartitionedQuery * Range splitting: add unit test for resultLimitReached * Range splitting: test frame merging * Chore: fix unit test --------- Co-authored-by: Sven Grossmann <svennergr@gmail.com> Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>
2 years ago
});