Skip to content

Commit 7b8533e

Browse files
authored
feat: Add step param to Patterns Query API (#12703)
1 parent 3bf2d1f commit 7b8533e

File tree

12 files changed

+343
-100
lines changed

12 files changed

+343
-100
lines changed

pkg/loghttp/patterns.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,27 @@ import (
99
func ParsePatternsQuery(r *http.Request) (*logproto.QueryPatternsRequest, error) {
1010
req := &logproto.QueryPatternsRequest{}
1111

12+
req.Query = query(r)
1213
start, end, err := bounds(r)
1314
if err != nil {
1415
return nil, err
1516
}
1617
req.Start = start
1718
req.End = end
1819

19-
req.Query = query(r)
20+
calculatedStep, err := step(r, start, end)
21+
if err != nil {
22+
return nil, err
23+
}
24+
if calculatedStep <= 0 {
25+
return nil, errZeroOrNegativeStep
26+
}
27+
// For safety, limit the number of returned points per timeseries.
28+
// This is sufficient for 60s resolution for a week or 1h resolution for a year.
29+
if (req.End.Sub(req.Start) / calculatedStep) > 11000 {
30+
return nil, errStepTooSmall
31+
}
32+
req.Step = calculatedStep.Milliseconds()
33+
2034
return req, nil
2135
}

pkg/loghttp/patterns_test.go

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package loghttp
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/grafana/loki/v3/pkg/logproto"
12+
)
13+
14+
func TestParsePatternsQuery(t *testing.T) {
15+
t.Parallel()
16+
17+
tests := []struct {
18+
name string
19+
path string
20+
want *logproto.QueryPatternsRequest
21+
wantErr bool
22+
}{
23+
{
24+
name: "should correctly parse valid params",
25+
path: "/loki/api/v1/patterns?query={}&start=100000000000&end=3600000000000&step=5s",
26+
want: &logproto.QueryPatternsRequest{
27+
Query: "{}",
28+
Start: time.Unix(100, 0),
29+
End: time.Unix(3600, 0),
30+
Step: (5 * time.Second).Milliseconds(),
31+
},
32+
},
33+
{
34+
name: "should default empty step param to sensible step for the range",
35+
path: "/loki/api/v1/patterns?query={}&start=100000000000&end=3600000000000",
36+
want: &logproto.QueryPatternsRequest{
37+
Query: "{}",
38+
Start: time.Unix(100, 0),
39+
End: time.Unix(3600, 0),
40+
Step: (14 * time.Second).Milliseconds(),
41+
},
42+
},
43+
{
44+
name: "should default start to zero for empty start param",
45+
path: "/loki/api/v1/patterns?query={}&end=3600000000000",
46+
want: &logproto.QueryPatternsRequest{
47+
Query: "{}",
48+
Start: time.Unix(0, 0),
49+
End: time.Unix(3600, 0),
50+
Step: (14 * time.Second).Milliseconds(),
51+
},
52+
},
53+
{
54+
name: "should accept step with no units as seconds",
55+
path: "/loki/api/v1/patterns?query={}&start=100000000000&end=3600000000000&step=10",
56+
want: &logproto.QueryPatternsRequest{
57+
Query: "{}",
58+
Start: time.Unix(100, 0),
59+
End: time.Unix(3600, 0),
60+
Step: (10 * time.Second).Milliseconds(),
61+
},
62+
},
63+
{
64+
name: "should accept step as string duration in seconds",
65+
path: "/loki/api/v1/patterns?query={}&start=100000000000&end=3600000000000&step=15s",
66+
want: &logproto.QueryPatternsRequest{
67+
Query: "{}",
68+
Start: time.Unix(100, 0),
69+
End: time.Unix(3600, 0),
70+
Step: (15 * time.Second).Milliseconds(),
71+
},
72+
},
73+
{
74+
name: "should correctly parse long duration for step",
75+
path: "/loki/api/v1/patterns?query={}&start=100000000000&end=3600000000000&step=10h",
76+
want: &logproto.QueryPatternsRequest{
77+
Query: "{}",
78+
Start: time.Unix(100, 0),
79+
End: time.Unix(3600, 0),
80+
Step: (10 * time.Hour).Milliseconds(),
81+
},
82+
},
83+
{
84+
name: "should reject negative step value",
85+
path: "/loki/api/v1/patterns?query={}&start=100000000000&end=3600000000000&step=-5s",
86+
want: nil,
87+
wantErr: true,
88+
},
89+
{
90+
name: "should reject very small step for big range",
91+
path: "/loki/api/v1/patterns?query={}&start=100000000000&end=3600000000000&step=50ms",
92+
want: nil,
93+
wantErr: true,
94+
},
95+
{
96+
name: "should accept very small step for small range",
97+
path: "/loki/api/v1/patterns?query={}&start=100000000000&end=110000000000&step=50ms",
98+
want: &logproto.QueryPatternsRequest{
99+
Query: "{}",
100+
Start: time.Unix(100, 0),
101+
End: time.Unix(110, 0),
102+
Step: (50 * time.Millisecond).Milliseconds(),
103+
},
104+
},
105+
}
106+
for _, tt := range tests {
107+
t.Run(tt.name, func(t *testing.T) {
108+
req, err := http.NewRequest(http.MethodGet, tt.path, nil)
109+
require.NoError(t, err)
110+
err = req.ParseForm()
111+
require.NoError(t, err)
112+
113+
got, err := ParsePatternsQuery(req)
114+
if tt.wantErr {
115+
require.Error(t, err)
116+
} else {
117+
require.NoError(t, err)
118+
}
119+
assert.Equalf(t, tt.want, got, "Incorrect response from input path: %s", tt.path)
120+
})
121+
}
122+
}

pkg/logproto/compat.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ func (m *VolumeRequest) LogToSpan(sp opentracing.Span) {
335335
otlog.String("query", m.GetQuery()),
336336
otlog.String("start", timestamp.Time(int64(m.From)).String()),
337337
otlog.String("end", timestamp.Time(int64(m.Through)).String()),
338+
otlog.String("step", time.Duration(m.Step).String()),
338339
)
339340
}
340341

@@ -448,8 +449,6 @@ func (m *ShardsRequest) LogToSpan(sp opentracing.Span) {
448449

449450
func (m *QueryPatternsRequest) GetCachingOptions() (res definitions.CachingOptions) { return }
450451

451-
func (m *QueryPatternsRequest) GetStep() int64 { return 0 }
452-
453452
func (m *QueryPatternsRequest) WithStartEnd(start, end time.Time) definitions.Request {
454453
clone := *m
455454
clone.Start = start
@@ -469,9 +468,10 @@ func (m *QueryPatternsRequest) WithStartEndForCache(start, end time.Time) result
469468

470469
func (m *QueryPatternsRequest) LogToSpan(sp opentracing.Span) {
471470
fields := []otlog.Field{
471+
otlog.String("query", m.GetQuery()),
472472
otlog.String("start", m.Start.String()),
473473
otlog.String("end", m.End.String()),
474-
otlog.String("query", m.GetQuery()),
474+
otlog.String("step", time.Duration(m.Step).String()),
475475
}
476476
sp.LogFields(fields...)
477477
}

pkg/logproto/pattern.pb.go

+73-32
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/logproto/pattern.proto

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ message QueryPatternsRequest {
2424
(gogoproto.stdtime) = true,
2525
(gogoproto.nullable) = false
2626
];
27+
int64 step = 4;
2728
}
2829

2930
message QueryPatternsResponse {

0 commit comments

Comments
 (0)