Skip to content

Commit 2940942

Browse files
authored
Merge pull request #1585 from cloudflare/tweaks
Fix sorting for range queries
2 parents 05ee0a7 + f7aaed4 commit 2940942

File tree

7 files changed

+79
-126
lines changed

7 files changed

+79
-126
lines changed

internal/checks/alerts_template.go

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"slices"
88
"strings"
9+
"sync"
910
textTemplate "text/template"
1011
"text/template/parse"
1112
"time"
@@ -35,7 +36,8 @@ var (
3536
"{{$externalURL := .ExternalURL}}",
3637
"{{$value := .Value}}",
3738
}
38-
templateDefsLen = len(strings.Join(templateDefs, ""))
39+
templateDefsString = strings.Join(templateDefs, "")
40+
templateDefsLen = len(templateDefsString)
3941

4042
templateFuncMap = textTemplate.FuncMap{
4143
"query": dummyFuncMap,
@@ -67,6 +69,16 @@ var (
6769
"externalURL": dummyFuncMap,
6870
"parseDuration": dummyFuncMap,
6971
}
72+
73+
templatePool = sync.Pool{
74+
New: func() any {
75+
t := textTemplate.
76+
New(TemplateCheckName).
77+
Funcs(templateFuncMap).
78+
Option("missingkey=zero")
79+
return t
80+
},
81+
}
7082
)
7183

7284
func dummyFuncMap(q string) string {
@@ -135,7 +147,7 @@ func (c TemplateCheck) Check(ctx context.Context, entry discovery.Entry, _ []dis
135147
},
136148
})
137149
}
138-
for _, msg := range checkForValueInLabels(label.Key.Value, label.Value.Value) {
150+
for _, msg := range checkForValueInLabels(label.Value.Value) {
139151
problems = append(problems, Problem{
140152
Anchor: AnchorAfter,
141153
Lines: diags.LineRange{
@@ -194,10 +206,10 @@ func (c TemplateCheck) Check(ctx context.Context, entry discovery.Entry, _ []dis
194206
}
195207

196208
func (c TemplateCheck) checkHumanizeIsNeeded(expr parser.PromQLExpr, ann *parser.YamlKeyValue) (problems []Problem) {
197-
if !hasValue(ann.Key.Value, ann.Value.Value) {
209+
if !hasValue(ann.Value.Value) {
198210
return problems
199211
}
200-
if hasHumanize(ann.Key.Value, ann.Value.Value) {
212+
if hasHumanize(ann.Value.Value) {
201213
return problems
202214
}
203215
vars, aliases, ok := findTemplateVariables(ann.Key.Value, ann.Value.Value)
@@ -295,7 +307,7 @@ func maybeExpandError(err error) error {
295307
func checkTemplateSyntax(ctx context.Context, name, text string, data any) error {
296308
tmpl := promTemplate.NewTemplateExpander(
297309
ctx,
298-
strings.Join(append(templateDefs, text), ""),
310+
templateDefsString+text,
299311
name,
300312
data,
301313
model.Time(timestamp.FromTime(time.Now())),
@@ -316,18 +328,17 @@ func checkTemplateSyntax(ctx context.Context, name, text string, data any) error
316328
return nil
317329
}
318330

319-
func checkForValueInLabels(name, text string) (msgs []string) {
320-
t, err := textTemplate.
321-
New(name).
322-
Funcs(templateFuncMap).
323-
Option("missingkey=zero").
324-
Parse(strings.Join(append(templateDefs, text), ""))
331+
func checkForValueInLabels(text string) (msgs []string) {
332+
t := templatePool.Get().(*textTemplate.Template)
333+
defer templatePool.Put(t)
334+
335+
tt, err := t.Parse(templateDefsString + text)
325336
if err != nil {
326337
// no need to double report errors
327338
return nil
328339
}
329-
aliases := aliasesForTemplate(t)
330-
for _, node := range t.Root.Nodes {
340+
aliases := aliasesForTemplate(tt)
341+
for _, node := range tt.Root.Nodes {
331342
if v, ok := containsAliasedNode(aliases, node, ".Value"); ok {
332343
msg := fmt.Sprintf("Using `%s` in labels will generate a new alert on every value change, move it to annotations.", v)
333344
msgs = append(msgs, msg)
@@ -348,17 +359,16 @@ func containsAliasedNode(am aliasMap, node parse.Node, alias string) (string, bo
348359
return "", false
349360
}
350361

351-
func hasValue(name, text string) bool {
352-
t, err := textTemplate.
353-
New(name).
354-
Funcs(templateFuncMap).
355-
Option("missingkey=zero").
356-
Parse(strings.Join(append(templateDefs, text), ""))
362+
func hasValue(text string) bool {
363+
t := templatePool.Get().(*textTemplate.Template)
364+
defer templatePool.Put(t)
365+
366+
tt, err := t.Parse(templateDefsString + text)
357367
if err != nil {
358368
// no need to double report errors
359369
return false
360370
}
361-
aliases := aliasesForTemplate(t)
371+
aliases := aliasesForTemplate(tt)
362372
for _, node := range t.Root.Nodes {
363373
if _, ok := containsAliasedNode(aliases, node, ".Value"); ok {
364374
return true
@@ -367,19 +377,18 @@ func hasValue(name, text string) bool {
367377
return false
368378
}
369379

370-
func hasHumanize(name, text string) bool {
371-
t, err := textTemplate.
372-
New(name).
373-
Funcs(templateFuncMap).
374-
Option("missingkey=zero").
375-
Parse(strings.Join(append(templateDefs, text), ""))
380+
func hasHumanize(text string) bool {
381+
t := templatePool.Get().(*textTemplate.Template)
382+
defer templatePool.Put(t)
383+
384+
tt, err := t.Parse(templateDefsString + text)
376385
if err != nil {
377386
// no need to double report errors
378387
return false
379388
}
380-
aliases := aliasesForTemplate(t)
389+
aliases := aliasesForTemplate(tt)
381390

382-
for _, node := range t.Root.Nodes {
391+
for _, node := range tt.Root.Nodes {
383392
if _, ok := containsAliasedNode(aliases, node, ".Value"); !ok {
384393
continue
385394
}
@@ -474,19 +483,18 @@ func getVariables(node parse.Node) (vars []tmplVar) {
474483
return vars
475484
}
476485

477-
func findTemplateVariables(name, text string) (vars []tmplVar, aliases aliasMap, ok bool) {
478-
t, err := textTemplate.
479-
New(name).
480-
Funcs(templateFuncMap).
481-
Option("missingkey=zero").
482-
Parse(strings.Join(append(templateDefs, text), ""))
486+
func findTemplateVariables(_, text string) (vars []tmplVar, aliases aliasMap, ok bool) {
487+
t := templatePool.Get().(*textTemplate.Template)
488+
defer templatePool.Put(t)
489+
490+
tt, err := t.Parse(templateDefsString + text)
483491
if err != nil {
484492
// no need to double report errors
485493
return vars, aliases, false
486494
}
487495

488496
aliases.aliases = map[string]map[string]struct{}{}
489-
for _, node := range t.Root.Nodes {
497+
for _, node := range tt.Root.Nodes {
490498
getAliases(node, &aliases)
491499
vars = append(vars, getVariables(node)...)
492500
}

internal/checks/promql_series.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,14 +260,14 @@ func (c SeriesCheck) Check(ctx context.Context, entry discovery.Entry, entries [
260260
if err != nil {
261261
slog.LogAttrs(ctx, slog.LevelWarn, "Cannot detect Prometheus uptime gaps", slog.Any("err", err), slog.String("name", c.prom.Name()))
262262
}
263-
if promUptime != nil && promUptime.Series.Ranges.Len() == 0 {
263+
if promUptime != nil && len(promUptime.Series.Ranges) == 0 {
264264
slog.LogAttrs(ctx, slog.LevelWarn,
265265
"No results for Prometheus uptime metric, you might have set uptime config option to a missing metric, please check your config",
266266
slog.String("name", c.prom.Name()),
267267
slog.String("metric", c.prom.UptimeMetric()),
268268
)
269269
}
270-
if promUptime == nil || promUptime.Series.Ranges.Len() == 0 {
270+
if promUptime == nil || len(promUptime.Series.Ranges) == 0 {
271271
slog.LogAttrs(ctx, slog.LevelWarn,
272272
"Using dummy Prometheus uptime metric results with no gaps",
273273
slog.String("name", c.prom.Name()),
@@ -401,7 +401,7 @@ func (c SeriesCheck) Check(ctx context.Context, entry discovery.Entry, entries [
401401
continue
402402
}
403403

404-
if trsLabelCount.Series.Ranges.Len() == 1 && len(trsLabelCount.Series.Gaps) == 0 {
404+
if len(trsLabelCount.Series.Ranges) == 1 && len(trsLabelCount.Series.Gaps) == 0 {
405405
problems = append(problems, Problem{
406406
Anchor: AnchorAfter,
407407
Lines: expr.Value.Pos.Lines(),

internal/config/rule.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"log/slog"
66
"regexp"
77
"slices"
8+
"strings"
89
"time"
910

1011
"github.com/cloudflare/pint/internal/checks"
@@ -148,10 +149,9 @@ func (rule Rule) validate() (err error) {
148149
}
149150

150151
func isDisabledForRule(rule parser.Rule, name string, check checks.RuleChecker, promTags []string) bool {
151-
matches := []string{
152-
name,
153-
check.String(),
154-
}
152+
matches := make([]string, 0, len(promTags)+2)
153+
matches = append(matches, name)
154+
matches = append(matches, check.String())
155155
for _, tag := range promTags {
156156
matches = append(matches, name+"(+"+tag+")")
157157
}
@@ -197,9 +197,11 @@ func isEnabled(enabledChecks, disabledChecks []string, rule parser.Rule, name st
197197
if c == name || c == check.String() {
198198
return false
199199
}
200-
for _, tag := range promTags {
201-
if c == name+"(+"+tag+")" {
202-
return false
200+
if strings.HasPrefix(c, name) {
201+
for _, tag := range promTags {
202+
if c == name+"(+"+tag+")" {
203+
return false
204+
}
203205
}
204206
}
205207
}

internal/parser/utils/source.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,7 @@ func parseCall(expr string, n *promParser.Call) (src []Source) {
895895
}
896896

897897
func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) {
898+
src = make([]Source, 0, 2)
898899
switch {
899900
// foo{} + 1
900901
// 1 + foo{}

internal/promapi/range.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"math"
1010
"net/http"
1111
"net/url"
12-
"sort"
12+
"slices"
1313
"strconv"
1414
"sync"
1515
"time"
@@ -101,13 +101,13 @@ func (prom *Prometheus) RangeQuery(ctx context.Context, expr string, params Rang
101101
lookback := params.Dur()
102102
step := params.Step()
103103

104-
var slices []TimeRange
104+
var timeSlices []TimeRange
105105
queryStep := (time.Hour * 2).Round(step)
106106
if queryStep > lookback {
107107
queryStep = lookback
108-
slices = append(slices, TimeRange{Start: start, End: end})
108+
timeSlices = append(timeSlices, TimeRange{Start: start, End: end})
109109
} else {
110-
slices = sliceRange(start, end, step, queryStep)
110+
timeSlices = sliceRange(start, end, step, queryStep)
111111
}
112112

113113
slog.LogAttrs(ctx, slog.LevelDebug, "Scheduling prometheus range query",
@@ -116,7 +116,7 @@ func (prom *Prometheus) RangeQuery(ctx context.Context, expr string, params Rang
116116
slog.String("lookback", output.HumanizeDuration(lookback)),
117117
slog.String("step", output.HumanizeDuration(step)),
118118
slog.String("slice", output.HumanizeDuration(queryStep)),
119-
slog.Int("slices", len(slices)),
119+
slog.Int("slices", len(timeSlices)),
120120
)
121121

122122
key := APIPathQueryRange + "\n" + expr + "\n" + params.String()
@@ -129,8 +129,8 @@ func (prom *Prometheus) RangeQuery(ctx context.Context, expr string, params Rang
129129
ctx, cancel := context.WithCancel(ctx)
130130
defer cancel()
131131

132-
results := make(chan queryResult, len(slices))
133-
for _, s := range slices {
132+
results := make(chan queryResult, len(timeSlices))
133+
for _, s := range timeSlices {
134134
query := queryRequest{ // nolint: exhaustruct
135135
query: rangeQuery{
136136
prom: prom,
@@ -200,7 +200,7 @@ func (prom *Prometheus) RangeQuery(ctx context.Context, expr string, params Rang
200200
return nil, QueryError{err: lastErr, msg: decodeError(lastErr)}
201201
}
202202

203-
sort.Stable(merged.Series.Ranges)
203+
slices.SortStableFunc(merged.Series.Ranges, CompareMetricTimeRanges)
204204

205205
slog.LogAttrs(ctx, slog.LevelDebug,
206206
"Parsed range response",

internal/promapi/range_normalize.go

Lines changed: 6 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -9,51 +9,6 @@ import (
99
"github.com/prometheus/prometheus/model/labels"
1010
)
1111

12-
func labelValue(ls labels.Labels, name string) (val string, ok bool) {
13-
ls.Range(func(l labels.Label) {
14-
if l.Name == name {
15-
val = l.Value
16-
ok = true
17-
}
18-
})
19-
return val, ok
20-
}
21-
22-
func labelsBefore(ls, o labels.Labels) bool {
23-
if ls.Len() < o.Len() {
24-
return true
25-
}
26-
if ls.Len() > o.Len() {
27-
return false
28-
}
29-
30-
lns := make([]string, 0, ls.Len()+o.Len())
31-
ls.Range(func(l labels.Label) {
32-
lns = append(lns, l.Name)
33-
})
34-
o.Range(func(l labels.Label) {
35-
lns = append(lns, l.Name)
36-
})
37-
slices.Sort(lns)
38-
for _, ln := range lns {
39-
mlv, ok := labelValue(ls, ln)
40-
if !ok {
41-
return true
42-
}
43-
olv, ok := labelValue(o, ln)
44-
if !ok {
45-
return false
46-
}
47-
if mlv < olv {
48-
return true
49-
}
50-
if mlv > olv {
51-
return false
52-
}
53-
}
54-
return false
55-
}
56-
5712
type TimeRange struct {
5813
Start time.Time
5914
End time.Time
@@ -182,19 +137,11 @@ func (mtr MetricTimeRanges) String() string {
182137
return strings.Join(sl, " ** ")
183138
}
184139

185-
func (mtr MetricTimeRanges) Len() int {
186-
return len(mtr)
187-
}
188-
189-
func (mtr MetricTimeRanges) Swap(i, j int) {
190-
mtr[i], mtr[j] = mtr[j], mtr[i]
191-
}
192-
193-
func (mtr MetricTimeRanges) Less(i, j int) bool {
194-
if mtr[i].Fingerprint != mtr[j].Fingerprint {
195-
return labelsBefore(mtr[i].Labels, mtr[j].Labels)
140+
func CompareMetricTimeRanges(a, b MetricTimeRange) int {
141+
if a.Fingerprint != b.Fingerprint {
142+
return labels.Compare(a.Labels, b.Labels)
196143
}
197-
return mtr[i].Start.Before(mtr[j].Start)
144+
return a.Start.Compare(b.Start)
198145
}
199146

200147
type SeriesTimeRanges struct {
@@ -241,19 +188,14 @@ func (str *SeriesTimeRanges) FindGaps(baseline SeriesTimeRanges, from, until tim
241188
// merge [t1:t2] [t2:t3] together.
242189
// This will sort the source slice.
243190
func MergeRanges(source MetricTimeRanges, step time.Duration) (MetricTimeRanges, bool) {
244-
slices.SortStableFunc(source, func(a, b MetricTimeRange) int {
245-
if a.Fingerprint != b.Fingerprint {
246-
return labels.Compare(a.Labels, b.Labels)
247-
}
248-
return a.Start.Compare(b.Start)
249-
})
191+
slices.SortStableFunc(source, CompareMetricTimeRanges)
250192

251193
var (
252194
ok, hadMerged bool
253195
tr TimeRange
254196
)
255197

256-
merged := make(MetricTimeRanges, 0, len(source)/4)
198+
merged := make(MetricTimeRanges, 0, len(source))
257199
L:
258200
for i := range source {
259201
for j := len(merged) - 1; j >= 0; j-- {

0 commit comments

Comments
 (0)