cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.59.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/failover.go

258lines · modecode

1package promapi
2
3import (
4 "context"
5 "log/slog"
6 "regexp"
7 "time"
8
9 "github.com/prometheus/client_golang/prometheus"
10)
11
12type FailoverGroupError struct {
13 err error
14 uri string
15 isStrict bool
16}
17
18func (e *FailoverGroupError) Unwrap() error {
19 return e.err
20}
21
22func (e *FailoverGroupError) Error() string {
23 return e.err.Error()
24}
25
26func (e *FailoverGroupError) URI() string {
27 return e.uri
28}
29
30func (e *FailoverGroupError) IsStrict() bool {
31 return e.isStrict
32}
33
34func cacheCleaner(cache *queryCache, interval time.Duration, quit chan bool) {
35 ticker := time.NewTicker(interval)
36 for {
37 select {
38 case <-quit:
39 return
40 case <-ticker.C:
41 cache.gc()
42 }
43 }
44}
45
46type FailoverGroup struct {
47 name string
48 publicURI string
49 servers []*Prometheus
50 uptimeMetric string
51 cacheCollector *cacheCollector
52 quitChan chan bool
53
54 pathsInclude []*regexp.Regexp
55 pathsExclude []*regexp.Regexp
56 tags []string
57 started bool
58 strictErrors bool
59}
60
61func NewFailoverGroup(name, publicURI string, servers []*Prometheus, strictErrors bool, uptimeMetric string, include, exclude []*regexp.Regexp, tags []string) *FailoverGroup {
62 return &FailoverGroup{
63 name: name,
64 publicURI: publicURI,
65 servers: servers,
66 strictErrors: strictErrors,
67 uptimeMetric: uptimeMetric,
68 pathsInclude: include,
69 pathsExclude: exclude,
70 tags: tags,
71 }
72}
73
74func (fg *FailoverGroup) Name() string {
75 return fg.name
76}
77
78func (fg *FailoverGroup) PublicURI() string {
79 return fg.publicURI
80}
81
82func (fg *FailoverGroup) Include() []string {
83 sl := []string{}
84 for _, re := range fg.pathsInclude {
85 sl = append(sl, re.String())
86 }
87 return sl
88}
89
90func (fg *FailoverGroup) Exclude() []string {
91 sl := []string{}
92 for _, re := range fg.pathsExclude {
93 sl = append(sl, re.String())
94 }
95 return sl
96}
97
98func (fg *FailoverGroup) Tags() []string {
99 return fg.tags
100}
101
102func (fg *FailoverGroup) UptimeMetric() string {
103 return fg.uptimeMetric
104}
105
106func (fg *FailoverGroup) ServerCount() int {
107 return len(fg.servers)
108}
109
110func (fg *FailoverGroup) MergeUpstreams(src *FailoverGroup) {
111 for _, ns := range src.servers {
112 var present bool
113 for _, ol := range fg.servers {
114 if ol.unsafeURI == ns.unsafeURI {
115 present = true
116 break
117 }
118 }
119 if !present {
120 fg.servers = append(fg.servers, ns)
121 slog.Debug(
122 "Added new failover URI",
123 slog.String("name", ns.name),
124 slog.String("uri", ns.safeURI),
125 )
126 }
127 }
128}
129
130func (fg *FailoverGroup) IsEnabledForPath(path string) bool {
131 if len(fg.pathsInclude) == 0 && len(fg.pathsExclude) == 0 {
132 return true
133 }
134 for _, re := range fg.pathsExclude {
135 if re.MatchString(path) {
136 return false
137 }
138 }
139 for _, re := range fg.pathsInclude {
140 if re.MatchString(path) {
141 return true
142 }
143 }
144 return false
145}
146
147func (fg *FailoverGroup) StartWorkers(reg *prometheus.Registry) {
148 if fg.started {
149 return
150 }
151
152 queryCache := newQueryCache(time.Hour)
153 fg.quitChan = make(chan bool)
154 go cacheCleaner(queryCache, time.Minute*2, fg.quitChan)
155
156 fg.cacheCollector = newCacheCollector(queryCache, fg.name)
157 reg.MustRegister(fg.cacheCollector)
158 for _, prom := range fg.servers {
159 prom.cache = queryCache
160 prom.StartWorkers()
161 }
162 fg.started = true
163}
164
165func (fg *FailoverGroup) Close(reg *prometheus.Registry) {
166 if !fg.started {
167 return
168 }
169 for _, prom := range fg.servers {
170 prom.Close()
171 }
172 reg.Unregister(fg.cacheCollector)
173 fg.quitChan <- true
174}
175
176func (fg *FailoverGroup) CleanCache() {
177 for _, prom := range fg.servers {
178 if prom.cache != nil {
179 prom.cache.gc()
180 return
181 }
182 }
183}
184
185func (fg *FailoverGroup) Config(ctx context.Context, cacheTTL time.Duration) (cfg *ConfigResult, err error) {
186 var uri string
187 for _, prom := range fg.servers {
188 uri = prom.safeURI
189 cfg, err = prom.Config(ctx, cacheTTL)
190 if err == nil {
191 return cfg, nil
192 }
193 if !IsUnavailableError(err) {
194 return nil, &FailoverGroupError{err: err, uri: uri, isStrict: fg.strictErrors}
195 }
196 }
197 return nil, &FailoverGroupError{err: err, uri: uri, isStrict: fg.strictErrors}
198}
199
200func (fg *FailoverGroup) Query(ctx context.Context, expr string) (qr *QueryResult, err error) {
201 var uri string
202 for _, prom := range fg.servers {
203 uri = prom.safeURI
204 qr, err = prom.Query(ctx, expr)
205 if err == nil {
206 return qr, nil
207 }
208 if !IsUnavailableError(err) {
209 return qr, &FailoverGroupError{err: err, uri: uri, isStrict: fg.strictErrors}
210 }
211 }
212 return nil, &FailoverGroupError{err: err, uri: uri, isStrict: fg.strictErrors}
213}
214
215func (fg *FailoverGroup) RangeQuery(ctx context.Context, expr string, params RangeQueryTimes) (rqr *RangeQueryResult, err error) {
216 var uri string
217 for _, prom := range fg.servers {
218 uri = prom.safeURI
219 rqr, err = prom.RangeQuery(ctx, expr, params)
220 if err == nil {
221 return rqr, nil
222 }
223 if !IsUnavailableError(err) {
224 return rqr, &FailoverGroupError{err: err, uri: uri, isStrict: fg.strictErrors}
225 }
226 }
227 return nil, &FailoverGroupError{err: err, uri: uri, isStrict: fg.strictErrors}
228}
229
230func (fg *FailoverGroup) Metadata(ctx context.Context, metric string) (metadata *MetadataResult, err error) {
231 var uri string
232 for _, prom := range fg.servers {
233 uri = prom.safeURI
234 metadata, err = prom.Metadata(ctx, metric)
235 if err == nil {
236 return metadata, nil
237 }
238 if !IsUnavailableError(err) {
239 return metadata, &FailoverGroupError{err: err, uri: uri, isStrict: fg.strictErrors}
240 }
241 }
242 return nil, &FailoverGroupError{err: err, uri: uri, isStrict: fg.strictErrors}
243}
244
245func (fg *FailoverGroup) Flags(ctx context.Context) (flags *FlagsResult, err error) {
246 var uri string
247 for _, prom := range fg.servers {
248 uri = prom.safeURI
249 flags, err = prom.Flags(ctx)
250 if err == nil {
251 return flags, nil
252 }
253 if !IsUnavailableError(err) {
254 return nil, &FailoverGroupError{err: err, uri: uri, isStrict: fg.strictErrors}
255 }
256 }
257 return nil, &FailoverGroupError{err: err, uri: uri, isStrict: fg.strictErrors}
258}
259