Skip to content

Commit 5e5b96d

Browse files
authored
Fixed query start/end timestamp rounding (#2990)
* Fixed query start/end timestamp rounding Signed-off-by: Marco Pracucci <[email protected]> * Added integration test Signed-off-by: Marco Pracucci <[email protected]> * Fixed linter Signed-off-by: Marco Pracucci <[email protected]> * Fixed integration test Signed-off-by: Marco Pracucci <[email protected]> * Improved tests Signed-off-by: Marco Pracucci <[email protected]>
1 parent 8d8975c commit 5e5b96d

File tree

5 files changed

+82
-4
lines changed

5 files changed

+82
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## master / unreleased
44

55
* [ENHANCEMENT] Query-tee: added a small tolerance to floating point sample values comparison. #2994
6+
* [BUGFIX] Query-frontend: Fixed rounding for incoming query timestamps, to be 100% Prometheus compatible. #2990
67

78
## 1.3.0 in progress
89

integration/e2ecortex/client.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,22 @@ func (c *Client) Push(timeseries []prompb.TimeSeries) (*http.Response, error) {
112112
return res, nil
113113
}
114114

115-
// Query runs a query
115+
// Query runs an instant query.
116116
func (c *Client) Query(query string, ts time.Time) (model.Value, error) {
117117
value, _, err := c.querierClient.Query(context.Background(), query, ts)
118118
return value, err
119119
}
120120

121+
// Query runs a query range.
122+
func (c *Client) QueryRange(query string, start, end time.Time, step time.Duration) (model.Value, error) {
123+
value, _, err := c.querierClient.QueryRange(context.Background(), query, promv1.Range{
124+
Start: start,
125+
End: end,
126+
Step: step,
127+
})
128+
return value, err
129+
}
130+
121131
func (c *Client) QueryRaw(query string) (*http.Response, []byte, error) {
122132
addr := fmt.Sprintf("http://%s/api/prom/api/v1/query?query=%s", c.querierAddress, url.QueryEscape(query))
123133

integration/query_frontend_test.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,30 @@ func runQueryFrontendTest(t *testing.T, testMissingMetricName bool, setup queryF
175175
c, err := e2ecortex.NewClient("", queryFrontend.HTTPEndpoint(), "", "", fmt.Sprintf("user-%d", userID))
176176
require.NoError(t, err)
177177

178-
// No need to repeat this test for each user.
178+
// No need to repeat the test on missing metric name for each user.
179179
if userID == 0 && testMissingMetricName {
180180
res, body, err := c.QueryRaw("{instance=~\"hello.*\"}")
181181
require.NoError(t, err)
182182
require.Equal(t, 422, res.StatusCode)
183183
require.Contains(t, string(body), "query must contain metric name")
184184
}
185185

186+
// No need to repeat the test on start/end time rounding for each user.
187+
if userID == 0 {
188+
start := time.Unix(1595846748, 806*1e6)
189+
end := time.Unix(1595846750, 806*1e6)
190+
191+
result, err := c.QueryRange("time()", start, end, time.Second)
192+
require.NoError(t, err)
193+
require.Equal(t, model.ValMatrix, result.Type())
194+
195+
matrix := result.(model.Matrix)
196+
require.Len(t, matrix, 1)
197+
require.Len(t, matrix[0].Values, 3)
198+
assert.Equal(t, model.Time(1595846748806), matrix[0].Values[0].Timestamp)
199+
assert.Equal(t, model.Time(1595846750806), matrix[0].Values[2].Timestamp)
200+
}
201+
186202
for q := 0; q < numQueriesPerUser; q++ {
187203
go func() {
188204
defer wg.Done()
@@ -197,9 +213,9 @@ func runQueryFrontendTest(t *testing.T, testMissingMetricName bool, setup queryF
197213

198214
wg.Wait()
199215

200-
extra := float64(0)
216+
extra := float64(1)
201217
if testMissingMetricName {
202-
extra = 1
218+
extra++
203219
}
204220
require.NoError(t, queryFrontend.WaitSumMetrics(e2e.Equals(numUsers*numQueriesPerUser+extra), "cortex_query_frontend_queries_total"))
205221

pkg/util/time.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func TimeFromMillis(ms int64) time.Time {
2727
func ParseTime(s string) (int64, error) {
2828
if t, err := strconv.ParseFloat(s, 64); err == nil {
2929
s, ns := math.Modf(t)
30+
ns = math.Round(ns*1000) / 1000
3031
tm := time.Unix(int64(s), int64(ns*float64(time.Second)))
3132
return TimeToMillis(tm), nil
3233
}

pkg/util/time_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,53 @@ func TestDurationWithJitter(t *testing.T) {
3838
func TestDurationWithJitter_ZeroInputDuration(t *testing.T) {
3939
assert.Equal(t, time.Duration(0), DurationWithJitter(time.Duration(0), 0.5))
4040
}
41+
42+
func TestParseTime(t *testing.T) {
43+
var tests = []struct {
44+
input string
45+
fail bool
46+
result time.Time
47+
}{
48+
{
49+
input: "",
50+
fail: true,
51+
}, {
52+
input: "abc",
53+
fail: true,
54+
}, {
55+
input: "30s",
56+
fail: true,
57+
}, {
58+
input: "123",
59+
result: time.Unix(123, 0),
60+
}, {
61+
input: "123.123",
62+
result: time.Unix(123, 123000000),
63+
}, {
64+
input: "2015-06-03T13:21:58.555Z",
65+
result: time.Unix(1433337718, 555*time.Millisecond.Nanoseconds()),
66+
}, {
67+
input: "2015-06-03T14:21:58.555+01:00",
68+
result: time.Unix(1433337718, 555*time.Millisecond.Nanoseconds()),
69+
}, {
70+
// Test nanosecond rounding.
71+
input: "2015-06-03T13:21:58.56789Z",
72+
result: time.Unix(1433337718, 567*1e6),
73+
}, {
74+
// Test float rounding.
75+
input: "1543578564.705",
76+
result: time.Unix(1543578564, 705*1e6),
77+
},
78+
}
79+
80+
for _, test := range tests {
81+
ts, err := ParseTime(test.input)
82+
if test.fail {
83+
require.Error(t, err)
84+
continue
85+
}
86+
87+
require.NoError(t, err)
88+
assert.Equal(t, TimeToMillis(test.result), ts)
89+
}
90+
}

0 commit comments

Comments
 (0)