cloudflare/pint

Public

mirrored fromhttps://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.75.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/cache.go

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