cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.71.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/cache.go

157lines · modecode

1package promapi
2
3import (
4 "sync"
5 "time"
6
7 "github.com/prometheus/client_golang/prometheus"
8)
9
10type cacheEntry struct {
11 data any
12 expiresAt time.Time
13 lastGet time.Time
14}
15
16type endpointStats struct {
17 hits int
18 misses int
19}
20
21func (e *endpointStats) hit() { e.hits++ }
22func (e *endpointStats) miss() { e.misses++ }
23
24func newQueryCache(maxStale time.Duration) *queryCache {
25 // nolint: exhaustruct
26 return &queryCache{
27 entries: map[uint64]*cacheEntry{},
28 stats: map[string]*endpointStats{},
29 maxStale: maxStale,
30 }
31}
32
33type queryCache struct {
34 entries map[uint64]*cacheEntry
35 stats map[string]*endpointStats
36 maxStale time.Duration
37 evictions int
38 mu sync.Mutex
39}
40
41func (c *queryCache) endpointStats(endpoint string) *endpointStats {
42 e, ok := c.stats[endpoint]
43 if ok {
44 return e
45 }
46
47 e = &endpointStats{hits: 0, misses: 0}
48 c.stats[endpoint] = e
49 return e
50}
51
52func (c *queryCache) get(key uint64, endpoint string) (v any, ok bool) {
53 c.mu.Lock()
54 defer c.mu.Unlock()
55
56 var ce *cacheEntry
57 ce, ok = c.entries[key]
58 if !ok {
59 c.endpointStats(endpoint).miss()
60 return v, ok
61 }
62
63 ce.lastGet = time.Now()
64 c.endpointStats(endpoint).hit()
65
66 return ce.data, true
67}
68
69// Cache results if it was requested at least twice EVER - which means it's either
70// popular and requested multiple times within a loop OR this cache key survives between loops.
71func (c *queryCache) set(key uint64, val any, ttl time.Duration) {
72 c.mu.Lock()
73 defer c.mu.Unlock()
74
75 c.entries[key] = &cacheEntry{
76 data: val,
77 lastGet: time.Now(),
78 expiresAt: time.Time{},
79 }
80 if ttl > 0 {
81 c.entries[key].expiresAt = time.Now().Add(ttl)
82 }
83}
84
85func (c *queryCache) gc() {
86 c.mu.Lock()
87 defer c.mu.Unlock()
88
89 entries := map[uint64]*cacheEntry{}
90
91 now := time.Now()
92 for key, ce := range c.entries {
93 if (!ce.expiresAt.IsZero() && ce.expiresAt.Before(now)) || now.Sub(ce.lastGet) >= c.maxStale {
94 c.evictions++
95 continue
96 }
97 entries[key] = ce
98 }
99 c.entries = entries
100}
101
102type cacheCollector struct {
103 cache *queryCache
104 entries *prometheus.Desc
105 hits *prometheus.Desc
106 misses *prometheus.Desc
107 evictions *prometheus.Desc
108}
109
110func newCacheCollector(cache *queryCache, name string) *cacheCollector {
111 return &cacheCollector{
112 cache: cache,
113 entries: prometheus.NewDesc(
114 "pint_prometheus_cache_size",
115 "Total number of entries currently stored in Prometheus query cache",
116 nil,
117 prometheus.Labels{"name": name},
118 ),
119 hits: prometheus.NewDesc(
120 "pint_prometheus_cache_hits_total",
121 "Total number of query cache hits",
122 []string{"endpoint"},
123 prometheus.Labels{"name": name},
124 ),
125 misses: prometheus.NewDesc(
126 "pint_prometheus_cache_miss_total",
127 "Total number of query cache misses",
128 []string{"endpoint"},
129 prometheus.Labels{"name": name},
130 ),
131 evictions: prometheus.NewDesc(
132 "pint_prometheus_cache_evictions_total",
133 "Total number of times an entry was evicted from query cache due to size limit or TTL",
134 nil,
135 prometheus.Labels{"name": name},
136 ),
137 }
138}
139
140func (c *cacheCollector) Describe(ch chan<- *prometheus.Desc) {
141 ch <- c.entries
142 ch <- c.hits
143 ch <- c.misses
144 ch <- c.evictions
145}
146
147func (c *cacheCollector) Collect(ch chan<- prometheus.Metric) {
148 c.cache.mu.Lock()
149 defer c.cache.mu.Unlock()
150 ch <- prometheus.MustNewConstMetric(c.entries, prometheus.GaugeValue, float64(len(c.cache.entries)))
151
152 for endpoint, stats := range c.cache.stats {
153 ch <- prometheus.MustNewConstMetric(c.hits, prometheus.CounterValue, float64(stats.hits), endpoint)
154 ch <- prometheus.MustNewConstMetric(c.misses, prometheus.CounterValue, float64(stats.misses), endpoint)
155 }
156 ch <- prometheus.MustNewConstMetric(c.evictions, prometheus.CounterValue, float64(c.cache.evictions))
157}
158