cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.11.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/range.go

107lines · modecode

1package promapi
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "net"
8 "strings"
9 "time"
10
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 (p *Prometheus) RangeQuery(ctx context.Context, expr string, start, end time.Time, step time.Duration) (*RangeQueryResult, error) {
24 log.Debug().
25 Str("uri", p.uri).
26 Str("query", expr).
27 Time("start", start).
28 Time("end", end).
29 Str("step", HumanizeDuration(step)).
30 Msg("Scheduling prometheus range query")
31
32 lockKey := "/api/v1/query/range"
33 p.lock.lock(lockKey)
34
35 cacheKey := strings.Join([]string{expr, start.String(), end.String(), step.String()}, "\n")
36 if v, ok := p.cache.Get(cacheKey); ok {
37 log.Debug().Str("key", cacheKey).Str("uri", p.uri).Msg("Range query cache hit")
38 r := v.(RangeQueryResult)
39 p.lock.unlock((lockKey))
40 return &r, nil
41 }
42
43 rctx, cancel := context.WithTimeout(ctx, p.timeout)
44 defer cancel()
45
46 prometheusQueriesTotal.WithLabelValues(p.name, "/api/v1/query_range").Inc()
47 r := v1.Range{
48 Start: start,
49 End: end,
50 Step: step,
51 }
52 qstart := time.Now()
53 result, _, err := p.api.QueryRange(rctx, expr, r)
54 duration := time.Since(qstart)
55 p.lock.unlock((lockKey))
56 log.Debug().
57 Str("uri", p.uri).
58 Str("query", expr).
59 Str("duration", HumanizeDuration(duration)).
60 Msg("Range query completed")
61 if err != nil {
62 log.Error().Err(err).Str("uri", p.uri).Str("query", expr).Msg("Range query failed")
63 prometheusQueryErrorsTotal.WithLabelValues(p.name, "/api/v1/query_range", errReason(err)).Inc()
64 if delta, retryOK := canRetry(err, end.Sub(start)); retryOK {
65 if delta < step*2 {
66 log.Error().Str("uri", p.uri).Str("query", expr).Msg("No more retries possible")
67 return nil, errors.New("no more retries possible")
68 }
69 log.Warn().Str("delta", HumanizeDuration(delta)).Msg("Retrying request with smaller range")
70 return p.RangeQuery(ctx, expr, start.Add(delta), end, step)
71 }
72 return nil, err
73 }
74
75 qr := RangeQueryResult{
76 DurationSeconds: duration.Seconds(),
77 Start: start,
78 End: end,
79 }
80
81 switch result.Type() {
82 case model.ValMatrix:
83 samples := result.(model.Matrix)
84 qr.Samples = samples
85 default:
86 log.Error().Err(err).Str("uri", p.uri).Str("query", expr).Msgf("Range query returned unknown result type: %v", result.Type())
87 prometheusQueryErrorsTotal.WithLabelValues(p.name, "/api/v1/query_range", "unknown result type").Inc()
88 return nil, fmt.Errorf("unknown result type: %v", result.Type())
89 }
90 log.Debug().Str("uri", p.uri).Str("query", expr).Int("samples", len(qr.Samples)).Msg("Parsed range response")
91
92 log.Debug().Str("query", expr).Str("uri", p.uri).Msg("Range query cache miss")
93 p.cache.Add(cacheKey, qr)
94
95 return &qr, nil
96}
97
98func canRetry(err error, delta time.Duration) (time.Duration, bool) {
99 var neterr net.Error
100 if ok := errors.As(err, &neterr); ok && neterr.Timeout() {
101 return delta / 2, true
102 }
103 if strings.Contains(err.Error(), "query processing would load too many samples into memory in ") {
104 return (delta / 4) * 3, true
105 }
106 return delta, false
107}
108