cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.1.4

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/range.go

108lines · modecode

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