diff --git a/pkg/query-service/postprocess/gaps.go b/pkg/query-service/postprocess/gaps.go index ff748cf05b..28c66eddb5 100644 --- a/pkg/query-service/postprocess/gaps.go +++ b/pkg/query-service/postprocess/gaps.go @@ -19,7 +19,7 @@ func StepIntervalForFunction(params *v3.QueryRangeParamsV3, query string) int64 return q.StepInterval } -func fillGap(series *v3.Series, start, end, step int64) *v3.Series { +func fillGap(series *v3.Series, start, end, step, shiftBy int64) *v3.Series { v := make(map[int64]float64) for _, point := range series.Points { v[point.Timestamp] = point.Value @@ -28,6 +28,8 @@ func fillGap(series *v3.Series, start, end, step int64) *v3.Series { // For all the values from start to end, find the timestamps // that don't have value and add zero point start = start - (start % (step * 1000)) + start += shiftBy * 1000 + end += shiftBy * 1000 for i := start; i <= end; i += step * 1000 { if _, ok := v[i]; !ok { v[i] = 0 @@ -65,8 +67,11 @@ func FillGaps(results []*v3.Result, params *v3.QueryRangeParamsV3) { if builderQueries != nil { // The values should be added at the intervals of `step` step := StepIntervalForFunction(params, result.QueryName) + shiftBy := builderQueries[result.QueryName].ShiftBy + start := params.Start - shiftBy*1000 + end := params.End - shiftBy*1000 for idx := range result.Series { - result.Series[idx] = fillGap(result.Series[idx], params.Start, params.End, step) + result.Series[idx] = fillGap(result.Series[idx], start, end, step, shiftBy) } } } diff --git a/pkg/query-service/postprocess/gaps_test.go b/pkg/query-service/postprocess/gaps_test.go index d89b2cbef6..61828b0c7e 100644 --- a/pkg/query-service/postprocess/gaps_test.go +++ b/pkg/query-service/postprocess/gaps_test.go @@ -178,14 +178,59 @@ func TestFillGaps(t *testing.T) { }), }, }, + { + name: "Single series with gaps and time shift", + results: []*v3.Result{ + createResult("query1", []*v3.Series{ + createSeries([]v3.Point{ + {Timestamp: 1747597560000, Value: 2.0}, // 19 May 2025 1:16:00 AM + }), + }), + }, + params: &v3.QueryRangeParamsV3{ + Start: 1747595100000, // 19 May 2025 12:35:00 AM + End: 1747599000000, // 19 May 2025 1:40:00 AM + CompositeQuery: &v3.CompositeQuery{ + PanelType: v3.PanelTypeGraph, + BuilderQueries: map[string]*v3.BuilderQuery{ + "query1": { + StepInterval: 840, // 14 minutes + QueryName: "query1", + Expression: "query1", + ShiftBy: 3600, // 1 hour + }, + }, + }, + }, + expected: []*v3.Result{ + createResult("query1", []*v3.Series{ + createSeries([]v3.Point{ + {Timestamp: 1747595040000, Value: 0.0}, // 19 May 2025 12:34:00 AM + {Timestamp: 1747595880000, Value: 0.0}, // 19 May 2025 12:48:00 AM + {Timestamp: 1747596720000, Value: 0.0}, // 19 May 2025 1:02:00 AM + {Timestamp: 1747597560000, Value: 2.0}, // 19 May 2025 1:16:00 AM + {Timestamp: 1747598400000, Value: 0.0}, // 19 May 2025 1:30:00 AM + }), + }), + }, + }, } // Execute test cases for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { FillGaps(tt.results, tt.params) + if len(tt.results) != len(tt.expected) { + t.Errorf("Test %s failed: expected %d results, got %d", tt.name, len(tt.expected), len(tt.results)) + } for i, result := range tt.results { + if len(result.Series) != len(tt.expected[i].Series) { + t.Errorf("Test %s failed: expected %d series, got %d", tt.name, len(tt.expected[i].Series), len(result.Series)) + } for j, series := range result.Series { + if len(series.Points) != len(tt.expected[i].Series[j].Points) { + t.Errorf("Test %s failed: expected %d points, got %d", tt.name, len(tt.expected[i].Series[j].Points), len(series.Points)) + } for k, point := range series.Points { if point.Timestamp != tt.expected[i].Series[j].Points[k].Timestamp || point.Value != tt.expected[i].Series[j].Points[k].Value {