diff --git a/packages/grafana-prometheus/src/querycache/QueryCache.test.ts b/packages/grafana-prometheus/src/querycache/QueryCache.test.ts index 28e10381b61..10462e3b3f4 100644 --- a/packages/grafana-prometheus/src/querycache/QueryCache.test.ts +++ b/packages/grafana-prometheus/src/querycache/QueryCache.test.ts @@ -646,7 +646,7 @@ describe('QueryCache: Prometheus', function () { }); describe('findDataPointStep', () => { - it('should interpolate custom interval', () => { + it('should interpolate custom interval when there is no calculatedMinStep in response', () => { const mockApplyInterpolation = jest.fn().mockImplementation(() => '1m'); const req = mockPromRequest(); req.targets[0].interval = '$interval'; @@ -654,4 +654,20 @@ describe('findDataPointStep', () => { findDatapointStep(req, respFrames, mockApplyInterpolation); expect(mockApplyInterpolation).toBeCalledTimes(1); }); + + it('should use the calculated minStep coming in response frame', () => { + const mockApplyInterpolation = jest.fn().mockImplementation(() => '1m'); + const req = mockPromRequest(); + // Cannot be interpolated on frontend as it was calculated on backend. + req.targets[0].interval = '$__rate_interval'; + const respFrames = trimmedFirstPointInPromFrames as unknown as DataFrame[]; + const expectedMinStep = 60000; + respFrames[0].meta = { + custom: { + calculatedMinStep: expectedMinStep, + }, + }; + const result = findDatapointStep(req, respFrames, mockApplyInterpolation); + expect(result).toBe(expectedMinStep); + }); }); diff --git a/packages/grafana-prometheus/src/querycache/QueryCache.ts b/packages/grafana-prometheus/src/querycache/QueryCache.ts index 3afc3344f20..419e59c6c01 100644 --- a/packages/grafana-prometheus/src/querycache/QueryCache.ts +++ b/packages/grafana-prometheus/src/querycache/QueryCache.ts @@ -282,7 +282,9 @@ export function findDatapointStep( let dataPointStep = request.intervalMs; if (target?.interval) { - const minStepMs = rangeUtil.intervalToMs(applyInterpolation(target.interval)); + const minStepMs = + respFrames[0].meta?.custom?.['calculatedMinStep'] ?? + rangeUtil.intervalToMs(applyInterpolation(target.interval, request.scopedVars)); if (minStepMs > request.intervalMs) { dataPointStep = minStepMs; } diff --git a/pkg/promlib/converter/prom.go b/pkg/promlib/converter/prom.go index 3fb5edb50c6..a341ecf72ca 100644 --- a/pkg/promlib/converter/prom.go +++ b/pkg/promlib/converter/prom.go @@ -1140,8 +1140,8 @@ streamField: return parsedLabelsMap, structuredMetadataMap, nil } -func resultTypeToCustomMeta(resultType string) map[string]string { - return map[string]string{"resultType": resultType} +func resultTypeToCustomMeta(resultType string) map[string]any { + return map[string]any{"resultType": resultType} } func timeFromFloat(fv float64) time.Time { diff --git a/pkg/promlib/models/result.go b/pkg/promlib/models/result.go index 3cefc7fb3d6..dd6bafa8027 100644 --- a/pkg/promlib/models/result.go +++ b/pkg/promlib/models/result.go @@ -20,7 +20,7 @@ func ResultTypeFromFrame(frame *data.Frame) ResultType { if frame.Meta.Custom == nil { return ResultTypeUnknown } - custom, ok := frame.Meta.Custom.(map[string]string) + custom, ok := frame.Meta.Custom.(map[string]any) if !ok { return ResultTypeUnknown } diff --git a/pkg/promlib/querydata/response.go b/pkg/promlib/querydata/response.go index f9253e5ccba..808d88e9423 100644 --- a/pkg/promlib/querydata/response.go +++ b/pkg/promlib/querydata/response.go @@ -54,6 +54,14 @@ func (s *QueryData) parseResponse(ctx context.Context, q *models.Query, res *htt addMetadataToMultiFrame(q, frame) if i == 0 { frame.Meta.ExecutedQueryString = executedQueryString(q) + if frame.Meta.Custom == nil { + frame.Meta.Custom = make(map[string]any) + } + if custom, ok := frame.Meta.Custom.(map[string]any); ok { + // This is required for incremental querying feature + // Knowing the calculated minStep is required for merging and caching the frames on frontend side + custom["calculatedMinStep"] = q.Step.Milliseconds() + } } } diff --git a/pkg/promlib/testdata/range_auto.result.golden.jsonc b/pkg/promlib/testdata/range_auto.result.golden.jsonc index 9bb659c7679..ad04ad76303 100644 --- a/pkg/promlib/testdata/range_auto.result.golden.jsonc +++ b/pkg/promlib/testdata/range_auto.result.golden.jsonc @@ -7,6 +7,7 @@ // 1 // ], // "custom": { +// "calculatedMinStep": 1000, // "resultType": "matrix" // }, // "executedQueryString": "Expr: histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[4s])) by (le))\nStep: 1s" @@ -44,6 +45,7 @@ 1 ], "custom": { + "calculatedMinStep": 1000, "resultType": "matrix" }, "executedQueryString": "Expr: histogram_quantile(0.95, sum(rate(tns_request_duration_seconds_bucket[4s])) by (le))\nStep: 1s" diff --git a/pkg/promlib/testdata/range_infinity.result.golden.jsonc b/pkg/promlib/testdata/range_infinity.result.golden.jsonc index db0f3edfbce..91240b239c4 100644 --- a/pkg/promlib/testdata/range_infinity.result.golden.jsonc +++ b/pkg/promlib/testdata/range_infinity.result.golden.jsonc @@ -7,6 +7,7 @@ // 1 // ], // "custom": { +// "calculatedMinStep": 1000, // "resultType": "matrix" // }, // "executedQueryString": "Expr: 1 / 0\nStep: 1s" @@ -37,6 +38,7 @@ 1 ], "custom": { + "calculatedMinStep": 1000, "resultType": "matrix" }, "executedQueryString": "Expr: 1 / 0\nStep: 1s" diff --git a/pkg/promlib/testdata/range_missing.result.golden.jsonc b/pkg/promlib/testdata/range_missing.result.golden.jsonc index 4272beb9993..f9cc45aed1a 100644 --- a/pkg/promlib/testdata/range_missing.result.golden.jsonc +++ b/pkg/promlib/testdata/range_missing.result.golden.jsonc @@ -7,6 +7,7 @@ // 1 // ], // "custom": { +// "calculatedMinStep": 1000, // "resultType": "matrix" // }, // "executedQueryString": "Expr: test1\nStep: 1s" @@ -37,6 +38,7 @@ 1 ], "custom": { + "calculatedMinStep": 1000, "resultType": "matrix" }, "executedQueryString": "Expr: test1\nStep: 1s" diff --git a/pkg/promlib/testdata/range_nan.result.golden.jsonc b/pkg/promlib/testdata/range_nan.result.golden.jsonc index 9da1ecfeca7..1a9ae359b61 100644 --- a/pkg/promlib/testdata/range_nan.result.golden.jsonc +++ b/pkg/promlib/testdata/range_nan.result.golden.jsonc @@ -7,6 +7,7 @@ // 1 // ], // "custom": { +// "calculatedMinStep": 1000, // "resultType": "matrix" // }, // "executedQueryString": "Expr: \nStep: 1s" @@ -37,6 +38,7 @@ 1 ], "custom": { + "calculatedMinStep": 1000, "resultType": "matrix" }, "executedQueryString": "Expr: \nStep: 1s" diff --git a/pkg/promlib/testdata/range_simple.result.golden.jsonc b/pkg/promlib/testdata/range_simple.result.golden.jsonc index a0fe631e2e3..3f68c6c818a 100644 --- a/pkg/promlib/testdata/range_simple.result.golden.jsonc +++ b/pkg/promlib/testdata/range_simple.result.golden.jsonc @@ -7,6 +7,7 @@ // 1 // ], // "custom": { +// "calculatedMinStep": 1000, // "resultType": "matrix" // }, // "executedQueryString": "Expr: \nStep: 1s" @@ -60,6 +61,7 @@ 1 ], "custom": { + "calculatedMinStep": 1000, "resultType": "matrix" }, "executedQueryString": "Expr: \nStep: 1s"