cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.10.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/range.go

104lines · 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 r := v1.Range{
47 Start: start,
48 End: end,
49 Step: step,
50 }
51 qstart := time.Now()
52 result, _, err := p.api.QueryRange(rctx, expr, r)
53 duration := time.Since(qstart)
54 p.lock.unlock((lockKey))
55 log.Debug().
56 Str("uri", p.uri).
57 Str("query", expr).
58 Str("duration", HumanizeDuration(duration)).
59 Msg("Range query completed")
60 if err != nil {
61 log.Error().Err(err).Str("uri", p.uri).Str("query", expr).Msg("Range query failed")
62 if delta, retryOK := canRetry(err, end.Sub(start)); retryOK {
63 if delta < step*2 {
64 log.Error().Str("uri", p.uri).Str("query", expr).Msg("No more retries possible")
65 return nil, errors.New("no more retries possible")
66 }
67 log.Warn().Str("delta", HumanizeDuration(delta)).Msg("Retrying request with smaller range")
68 return p.RangeQuery(ctx, expr, start.Add(delta), end, step)
69 }
70 return nil, err
71 }
72
73 qr := RangeQueryResult{
74 DurationSeconds: duration.Seconds(),
75 Start: start,
76 End: end,
77 }
78
79 switch result.Type() {
80 case model.ValMatrix:
81 samples := result.(model.Matrix)
82 qr.Samples = samples
83 default:
84 log.Error().Err(err).Str("uri", p.uri).Str("query", expr).Msgf("Range query returned unknown result type: %v", result.Type())
85 return nil, fmt.Errorf("unknown result type: %v", result.Type())
86 }
87 log.Debug().Str("uri", p.uri).Str("query", expr).Int("samples", len(qr.Samples)).Msg("Parsed range response")
88
89 log.Debug().Str("query", expr).Str("uri", p.uri).Msg("Range query cache miss")
90 p.cache.Add(cacheKey, qr)
91
92 return &qr, nil
93}
94
95func canRetry(err error, delta time.Duration) (time.Duration, bool) {
96 var neterr net.Error
97 if ok := errors.As(err, &neterr); ok && neterr.Timeout() {
98 return delta / 2, true
99 }
100 if strings.Contains(err.Error(), "query processing would load too many samples into memory in ") {
101 return (delta / 4) * 3, true
102 }
103 return delta, false
104}
105