Loki: Fix metric time splitting to split starting with the start time (#80085)

* Loki: Fix metric time splitting to split starting with the start time

* add test case to gdev-dashboard

* fix splitting test
This commit is contained in:
Sven Grossmann 2024-01-08 13:26:16 +01:00 committed by GitHub
parent f0cb88e3b5
commit d1b0e9082d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 1205 additions and 941 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,55 @@
import { splitTimeRange } from './metricTimeSplitting'; import { splitTimeRange } from './metricTimeSplitting';
describe('metric splitTimeRange', () => { describe('metric splitTimeRange', () => {
it('should split time range into chunks', () => { it('should split time range into chunks with 1day split and duration', () => {
const start = Date.parse('2022-02-06T14:10:03'); const start = Date.parse('2022-02-06T14:10:03Z');
const end = Date.parse('2022-02-06T14:11:03'); const end = Date.parse('2022-02-08T14:11:03Z');
const step = 10 * 1000; const step = 24 * 60 * 60 * 1000; // 1 day
const rangeDuration = 24 * 60 * 60 * 1000; // 1 day
expect(splitTimeRange(start, end, step, 25000)).toStrictEqual([ expect(splitTimeRange(start, end, step, rangeDuration)).toStrictEqual([
[Date.parse('2022-02-06T14:10:00'), Date.parse('2022-02-06T14:10:10')], [Date.parse('2022-02-06T00:00:00Z'), Date.parse('2022-02-06T00:00:00Z')],
[Date.parse('2022-02-06T14:10:20'), Date.parse('2022-02-06T14:10:40')], [Date.parse('2022-02-07T00:00:00Z'), Date.parse('2022-02-07T00:00:00Z')],
[Date.parse('2022-02-06T14:10:50'), Date.parse('2022-02-06T14:11:10')], [Date.parse('2022-02-08T00:00:00Z'), Date.parse('2022-02-08T00:00:00Z')],
]);
});
it('should split time range into chunks with 1day split and duration and a 5 minute duration', () => {
const start = Date.parse('2022-02-06T14:00:00Z');
const end = Date.parse('2022-02-06T14:05:00Z');
const step = 24 * 60 * 60 * 1000; // 1 day
const rangeDuration = 24 * 60 * 60 * 1000; // 1 day
expect(splitTimeRange(start, end, step, rangeDuration)).toStrictEqual([
[Date.parse('2022-02-06T00:00:00Z'), Date.parse('2022-02-06T00:00:00Z')],
]);
});
it('should split time range into chunks with 1hour split and 1day duration', () => {
const start = Date.parse('2022-02-06T14:10:03Z');
const end = Date.parse('2022-02-08T14:11:03Z');
const step = 60 * 60 * 1000; // 1 hour
const rangeDuration = 24 * 60 * 60 * 1000; // 1 day
expect(splitTimeRange(start, end, step, rangeDuration)).toStrictEqual([
[Date.parse('2022-02-06T14:00:00Z'), Date.parse('2022-02-07T13:00:00Z')],
[Date.parse('2022-02-07T14:00:00Z'), Date.parse('2022-02-08T13:00:00Z')],
[Date.parse('2022-02-08T14:00:00Z'), Date.parse('2022-02-08T14:11:03Z')],
]);
});
it('should split time range into chunks with 1hour split and 12h duration', () => {
const start = Date.parse('2022-02-06T14:10:03Z');
const end = Date.parse('2022-02-08T14:11:03Z');
const step = 60 * 60 * 1000; // 1 hour
const rangeDuration = 12 * 60 * 60 * 1000; // 12h
expect(splitTimeRange(start, end, step, rangeDuration)).toStrictEqual([
[Date.parse('2022-02-06T14:00:00Z'), Date.parse('2022-02-07T01:00:00Z')],
[Date.parse('2022-02-07T02:00:00Z'), Date.parse('2022-02-07T13:00:00Z')],
[Date.parse('2022-02-07T14:00:00Z'), Date.parse('2022-02-08T01:00:00Z')],
[Date.parse('2022-02-08T02:00:00Z'), Date.parse('2022-02-08T13:00:00Z')],
[Date.parse('2022-02-08T14:00:00Z'), Date.parse('2022-02-08T14:11:03Z')],
]); ]);
}); });

View File

@ -5,20 +5,6 @@
// we are trying to be compatible with // we are trying to be compatible with
// https://github.com/grafana/loki/blob/089ec1b05f5ec15a8851d0e8230153e0eeb4dcec/pkg/querier/queryrange/split_by_interval.go#L327-L336 // https://github.com/grafana/loki/blob/089ec1b05f5ec15a8851d0e8230153e0eeb4dcec/pkg/querier/queryrange/split_by_interval.go#L327-L336
function expandTimeRange(startTime: number, endTime: number, step: number): [number, number] {
// startTime is decreased to the closes multiple-of-step, if necessary
const newStartTime = startTime - (startTime % step);
// endTime is increased to the closed multiple-of-step, if necessary
let newEndTime = endTime;
const endStepMod = endTime % step;
if (endStepMod !== 0) {
newEndTime += step - endStepMod;
}
return [newStartTime, newEndTime];
}
export function splitTimeRange( export function splitTimeRange(
startTime: number, startTime: number,
endTime: number, endTime: number,
@ -33,20 +19,19 @@ export function splitTimeRange(
// we make the duration a multiple of `step`, lowering it if necessary // we make the duration a multiple of `step`, lowering it if necessary
const alignedDuration = Math.trunc(idealRangeDuration / step) * step; const alignedDuration = Math.trunc(idealRangeDuration / step) * step;
const [alignedStartTime, alignedEndTime] = expandTimeRange(startTime, endTime, step); const alignedStartTime = startTime - (startTime % step);
const result: Array<[number, number]> = []; const result: Array<[number, number]> = [];
// we iterate it from the end, because we want to have the potentially smaller chunk at the end, not at the beginning // in a previous version we started iterating from the end, to the start.
for (let chunkEndTime = alignedEndTime; chunkEndTime > alignedStartTime; chunkEndTime -= alignedDuration + step) { // However this is not easily possible as end timestamps are always inclusive
// when we get close to the start of the time range, we need to be sure not // for Loki. So a `2022-02-08T00:00:00Z` end time with a 1day step would mean
// to cross over the startTime // to include the 08.02.2022, which we don't want. So we have to start from
const chunkStartTime = Math.max(chunkEndTime - alignedDuration, alignedStartTime); // the start, always ending at the last step before the actual end, or the total end.
for (let chunkStartTime = alignedStartTime; chunkStartTime < endTime; chunkStartTime += alignedDuration) {
const chunkEndTime = Math.min(chunkStartTime + alignedDuration - step, endTime);
result.push([chunkStartTime, chunkEndTime]); result.push([chunkStartTime, chunkEndTime]);
} }
// because we walked backwards, we need to reverse the array
result.reverse();
return result; return result;
} }

View File

@ -82,8 +82,8 @@ describe('runSplitQuery()', () => {
intervalMs: 60000, intervalMs: 60000,
range: expect.objectContaining({ range: expect.objectContaining({
from: expect.objectContaining({ from: expect.objectContaining({
//2023-02-09T06:00:00.000Z //2023-02-10T05:00:00.000Z
_i: 1675922400000, _i: 1676005200000,
}), }),
to: expect.objectContaining({ to: expect.objectContaining({
// 2023-02-10T06:00:00.000Z // 2023-02-10T06:00:00.000Z
@ -100,12 +100,12 @@ describe('runSplitQuery()', () => {
intervalMs: 60000, intervalMs: 60000,
range: expect.objectContaining({ range: expect.objectContaining({
from: expect.objectContaining({ from: expect.objectContaining({
//2023-02-08T05:59:00.000Z // 2023-02-09T05:00:00.000Z
_i: 1675835940000, _i: 1675918800000,
}), }),
to: expect.objectContaining({ to: expect.objectContaining({
// 2023-02-09T05:59:00.000Z // 2023-02-10T04:59:00.000Z
_i: 1675922340000, _i: 1676005140000,
}), }),
}), }),
}) })
@ -118,12 +118,12 @@ describe('runSplitQuery()', () => {
intervalMs: 60000, intervalMs: 60000,
range: expect.objectContaining({ range: expect.objectContaining({
from: expect.objectContaining({ from: expect.objectContaining({
//2023-02-08T05:00:00.000Z // 2023-02-08T05:00:00.000Z
_i: 1675832400000, _i: 1675832400000,
}), }),
to: expect.objectContaining({ to: expect.objectContaining({
// 2023-02-08T05:58:00.000Z // 2023-02-09T04:59:00.000Z
_i: 1675835880000, _i: 1675918740000,
}), }),
}), }),
}) })
@ -142,8 +142,8 @@ describe('runSplitQuery()', () => {
intervalMs: 60000, intervalMs: 60000,
range: expect.objectContaining({ range: expect.objectContaining({
from: expect.objectContaining({ from: expect.objectContaining({
//2023-02-09T06:00:00.000Z //2023-02-10T05:00:00.000Z
_i: 1675922400000, _i: 1676005200000,
}), }),
to: expect.objectContaining({ to: expect.objectContaining({
// 2023-02-10T06:00:00.000Z // 2023-02-10T06:00:00.000Z
@ -160,12 +160,12 @@ describe('runSplitQuery()', () => {
intervalMs: 60000, intervalMs: 60000,
range: expect.objectContaining({ range: expect.objectContaining({
from: expect.objectContaining({ from: expect.objectContaining({
//2023-02-08T05:59:50.000Z // 2023-02-09T05:00:00.000Z
_i: 1675835990000, _i: 1675918800000,
}), }),
to: expect.objectContaining({ to: expect.objectContaining({
// 2023-02-09T05:59:50.000Z // 2023-02-10T04:59:50.000Z
_i: 1675922390000, _i: 1676005190000,
}), }),
}), }),
}) })
@ -182,8 +182,8 @@ describe('runSplitQuery()', () => {
_i: 1675832400000, _i: 1675832400000,
}), }),
to: expect.objectContaining({ to: expect.objectContaining({
// 2023-02-08T05:59:40.000Z // 2023-02-09T04:59:50.000Z
_i: 1675835980000, _i: 1675918790000,
}), }),
}), }),
}) })