cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.4.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/promapi/range.go

110lines · modecode

1package promapi
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "net"
8 "strings"
9 "time"
10
11 "github.com/prometheus/client_golang/api"
12 v1 "github.com/prometheus/client_golang/api/prometheus/v1"
13 "github.com/prometheus/common/model"
14 "github.com/rs/zerolog/log"
15)
16
17type RangeQueryResult struct {
18 Samples []*model.SampleStream
19 Start time.Time
20 End time.Time
21 DurationSeconds float64
22}
23
24func RangeQuery(uri string, timeout time.Duration, expr string, start, end time.Time, step time.Duration, lockKey *string) (*RangeQueryResult, error) {
25 key := uri
26 if lockKey != nil {
27 key = *lockKey
28 }
29
30 log.Debug().
31 Str("key", key).
32 Str("uri", uri).
33 Str("query", expr).
34 Time("start", start).
35 Time("end", end).
36 Str("step", HumanizeDuration(step)).
37 Msg("Scheduling prometheus range query")
38
39 km.Lock(key)
40 defer km.Unlock((key))
41
42 log.Debug().Str("uri", uri).Str("query", expr).Msg("Range query started")
43
44 client, err := api.NewClient(api.Config{Address: uri})
45 if err != nil {
46 log.Error().Err(err).Msg("Failed to setup new Prometheus API client")
47 return nil, err
48 }
49
50 v1api := v1.NewAPI(client)
51 ctx, cancel := context.WithTimeout(context.Background(), timeout)
52 defer cancel()
53
54 r := v1.Range{
55 Start: start,
56 End: end,
57 Step: step,
58 }
59 qstart := time.Now()
60 result, _, err := v1api.QueryRange(ctx, expr, r)
61 duration := time.Since(qstart)
62 log.Debug().
63 Str("uri", uri).
64 Str("query", expr).
65 Str("duration", HumanizeDuration(duration)).
66 Msg("Range query completed")
67 if err != nil {
68 log.Error().Err(err).Str("uri", uri).Str("query", expr).Msg("Range query failed")
69 if isRetryable(err) {
70 delta := end.Sub(start) / 2
71 log.Warn().Str("delta", HumanizeDuration(delta)).Msg("Retrying request with smaller range")
72 b, _ := start.MarshalText()
73 newKey := fmt.Sprintf("%s/retry/%s", key, string(b))
74 return RangeQuery(uri, timeout, expr, start.Add(delta), end, step, &newKey)
75 }
76 return nil, err
77 }
78
79 qr := RangeQueryResult{
80 DurationSeconds: duration.Seconds(),
81 Start: start,
82 End: end,
83 }
84
85 switch result.Type() {
86 case model.ValMatrix:
87 samples := result.(model.Matrix)
88 qr.Samples = samples
89
90 case model.ValString:
91 fmt.Println("ValString")
92 default:
93 log.Error().Err(err).Str("uri", uri).Str("query", expr).Msgf("Range query returned unknown result type: %v", result)
94 return nil, fmt.Errorf("unknown result type: %v", result)
95 }
96 log.Debug().Str("uri", uri).Str("query", expr).Int("samples", len(qr.Samples)).Msg("Parsed range response")
97
98 return &qr, nil
99}
100
101func isRetryable(err error) bool {
102 var neterr net.Error
103 if ok := errors.As(err, &neterr); ok && neterr.Timeout() {
104 return true
105 }
106 if strings.Contains(err.Error(), "query processing would load too many samples into memory in ") {
107 return true
108 }
109 return false
110}
111