cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.77.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/cache_test.go

355lines · modecode

1package promapi
2
3import (
4 "errors"
5 "fmt"
6 "strings"
7 "testing"
8 "time"
9
10 "github.com/prometheus/client_golang/prometheus/testutil"
11 "github.com/stretchr/testify/require"
12)
13
14func TestQueryCacheOnlySet(t *testing.T) {
15 mockErr := errors.New("Fake Error")
16 cache := newQueryCache(time.Minute, time.Now)
17
18 var i uint64
19 for i = 1; i <= 100; i++ {
20 cache.set(i, mockErr, 0)
21 }
22
23 require.Len(t, cache.entries, 100)
24 require.Equal(t, 0, cache.evictions)
25}
26
27func TestQueryCacheReplace(t *testing.T) {
28 mockErr := errors.New("Fake Error")
29 cache := newQueryCache(time.Minute, time.Now)
30
31 cache.set(6, mockErr, 0)
32 cache.set(6, mockErr, 0)
33 cache.set(6, mockErr, 0)
34
35 require.Len(t, cache.entries, 1)
36 require.Equal(t, 0, cache.evictions)
37}
38
39func TestQueryCacheGetAndSet(t *testing.T) {
40 mockErr := errors.New("Fake Error")
41 cache := newQueryCache(time.Minute, time.Now)
42
43 var i uint64
44 for i = 1; i <= 100; i++ {
45 // first get
46 v, ok := cache.get(i, "/foo")
47 require.False(t, ok, "should be missing from cache on first get")
48 require.Zero(t, v)
49
50 // first set
51 cache.set(i, mockErr, time.Minute)
52
53 // second get, should be in cache now
54 v, ok = cache.get(i, "/foo")
55 require.True(t, ok, "should be present in cache on third get")
56 require.NotZero(t, v)
57 require.Equal(t, mockErr, v)
58 }
59
60 require.Len(t, cache.entries, 100)
61 require.Equal(t, 100, cache.stats["/foo"].hits)
62 require.Equal(t, 100, cache.stats["/foo"].misses)
63 require.Equal(t, 0, cache.evictions)
64
65 cache.gc()
66 require.Len(t, cache.entries, 100)
67 require.Equal(t, 100, cache.stats["/foo"].hits)
68 require.Equal(t, 100, cache.stats["/foo"].misses)
69 require.Equal(t, 0, cache.evictions)
70}
71
72func TestQueryCachePurgeZeroTTL(t *testing.T) {
73 const maxSize = 100
74 mockErr := errors.New("Fake Error")
75 cache := newQueryCache(time.Minute, time.Now)
76
77 var i uint64
78 for i = 1; i <= maxSize; i++ {
79 cache.set(i, mockErr, 0)
80 _, _ = cache.get(i, "/foo")
81 }
82 require.Len(t, cache.entries, 100)
83 require.Equal(t, 0, cache.evictions)
84
85 time.Sleep(time.Second)
86
87 cache.gc()
88 require.Len(t, cache.entries, 100)
89 require.Equal(t, 0, cache.evictions)
90}
91
92func TestQueryCachePurgeExpired(t *testing.T) {
93 const maxSize = 100
94 mockErr := errors.New("Fake Error")
95 cache := newQueryCache(time.Minute, time.Now)
96
97 var i uint64
98 for i = 1; i <= maxSize; i++ {
99 _, _ = cache.get(i, "/foo")
100 _, _ = cache.get(i, "/foo")
101 cache.set(i, mockErr, time.Second)
102 _, _ = cache.get(i, "/foo")
103 }
104 require.Len(t, cache.entries, 100)
105 require.Equal(t, 0, cache.evictions)
106
107 for i = 1; i <= maxSize/2; i++ {
108 cache.entries[i].expiresAt = time.Now().Add(time.Second * -1)
109 }
110
111 cache.gc()
112 require.Len(t, cache.entries, 50)
113 require.Equal(t, 50, cache.evictions)
114}
115
116func TestQueryCacheEvictMaxStale(t *testing.T) {
117 mockErr := errors.New("Fake Error")
118 cache := newQueryCache(time.Second, time.Now)
119
120 var i, j uint64
121 for i = 1; i <= 100; i++ {
122 cache.set(i, mockErr, time.Minute)
123 for j = 1; j <= i; j++ {
124 _, _ = cache.get(i, "/foo")
125 }
126 }
127 require.Len(t, cache.entries, 100)
128 require.Equal(t, 0, cache.evictions)
129
130 cache.gc()
131 require.Len(t, cache.entries, 100)
132 require.Equal(t, 0, cache.evictions)
133
134 time.Sleep(time.Second + time.Millisecond*100)
135 for i = 1; i <= 50; i++ {
136 _, _ = cache.get(i, "/foo")
137 }
138 cache.gc()
139 require.Len(t, cache.entries, 50)
140 require.Equal(t, 50, cache.evictions)
141
142 var ok bool
143 for i = 1; i <= 50; i++ {
144 _, ok = cache.get(i, "/foo")
145 require.True(t, ok)
146 }
147 for i = 51; i <= 100; i++ {
148 _, ok = cache.get(i, "/foo")
149 require.False(t, ok)
150 }
151}
152
153func TestCacheCollector(t *testing.T) {
154 cache := newQueryCache(time.Minute, time.Now)
155
156 names := []string{
157 "pint_prometheus_cache_size",
158 "pint_prometheus_cache_capacity",
159 "pint_prometheus_cache_hits_total",
160 "pint_prometheus_cache_miss_total",
161 "pint_prometheus_cache_evictions_total",
162 }
163
164 collector := newCacheCollector(cache, "prom")
165 require.NoError(t, testutil.CollectAndCompare(
166 collector, strings.NewReader(`
167# HELP pint_prometheus_cache_evictions_total Total number of times an entry was evicted from query cache due to size limit or TTL.
168# TYPE pint_prometheus_cache_evictions_total counter
169pint_prometheus_cache_evictions_total{name="prom"} 0
170# HELP pint_prometheus_cache_size Total number of entries currently stored in Prometheus query cache.
171# TYPE pint_prometheus_cache_size gauge
172pint_prometheus_cache_size{name="prom"} 0
173# HELP pint_prometheus_cache_capacity The capacity of Prometheus query cache.
174# TYPE pint_prometheus_cache_capacity gauge
175pint_prometheus_cache_capacity{name="prom"} 0
176`),
177 "pint_prometheus_cache_size", "pint_prometheus_cache_capacity", "pint_prometheus_cache_evictions_total",
178 ))
179
180 var i uint64
181 for i = 1; i <= 100; i++ {
182 endpoint := fmt.Sprintf("/foo/%d", i%10)
183 _, _ = cache.get(i, endpoint)
184 _, _ = cache.get(i, endpoint)
185 cache.set(i, queryResult{}, time.Minute)
186 _, _ = cache.get(i, endpoint)
187 cache.set(i, queryResult{}, time.Minute)
188 _, _ = cache.get(i, endpoint)
189 }
190
191 require.NoError(t, testutil.CollectAndCompare(
192 collector, strings.NewReader(`
193# HELP pint_prometheus_cache_evictions_total Total number of times an entry was evicted from query cache due to size limit or TTL.
194# TYPE pint_prometheus_cache_evictions_total counter
195pint_prometheus_cache_evictions_total{name="prom"} 0
196# HELP pint_prometheus_cache_hits_total Total number of query cache hits.
197# TYPE pint_prometheus_cache_hits_total counter
198pint_prometheus_cache_hits_total{endpoint="/foo/0",name="prom"} 20
199pint_prometheus_cache_hits_total{endpoint="/foo/1",name="prom"} 20
200pint_prometheus_cache_hits_total{endpoint="/foo/2",name="prom"} 20
201pint_prometheus_cache_hits_total{endpoint="/foo/3",name="prom"} 20
202pint_prometheus_cache_hits_total{endpoint="/foo/4",name="prom"} 20
203pint_prometheus_cache_hits_total{endpoint="/foo/5",name="prom"} 20
204pint_prometheus_cache_hits_total{endpoint="/foo/6",name="prom"} 20
205pint_prometheus_cache_hits_total{endpoint="/foo/7",name="prom"} 20
206pint_prometheus_cache_hits_total{endpoint="/foo/8",name="prom"} 20
207pint_prometheus_cache_hits_total{endpoint="/foo/9",name="prom"} 20
208# HELP pint_prometheus_cache_miss_total Total number of query cache misses.
209# TYPE pint_prometheus_cache_miss_total counter
210pint_prometheus_cache_miss_total{endpoint="/foo/0",name="prom"} 20
211pint_prometheus_cache_miss_total{endpoint="/foo/1",name="prom"} 20
212pint_prometheus_cache_miss_total{endpoint="/foo/2",name="prom"} 20
213pint_prometheus_cache_miss_total{endpoint="/foo/3",name="prom"} 20
214pint_prometheus_cache_miss_total{endpoint="/foo/4",name="prom"} 20
215pint_prometheus_cache_miss_total{endpoint="/foo/5",name="prom"} 20
216pint_prometheus_cache_miss_total{endpoint="/foo/6",name="prom"} 20
217pint_prometheus_cache_miss_total{endpoint="/foo/7",name="prom"} 20
218pint_prometheus_cache_miss_total{endpoint="/foo/8",name="prom"} 20
219pint_prometheus_cache_miss_total{endpoint="/foo/9",name="prom"} 20
220# HELP pint_prometheus_cache_size Total number of entries currently stored in Prometheus query cache.
221# TYPE pint_prometheus_cache_size gauge
222pint_prometheus_cache_size{name="prom"} 100
223# HELP pint_prometheus_cache_capacity The capacity of Prometheus query cache.
224# TYPE pint_prometheus_cache_capacity gauge
225pint_prometheus_cache_capacity{name="prom"} 100
226`),
227 names...,
228 ))
229
230 for i = 101; i <= 110; i++ {
231 endpoint := fmt.Sprintf("/foo/%d", i%10)
232 _, _ = cache.get(i, endpoint)
233 _, _ = cache.get(i, endpoint)
234 cache.set(i, queryResult{}, time.Minute)
235 }
236
237 require.NoError(t, testutil.CollectAndCompare(
238 collector, strings.NewReader(`
239# HELP pint_prometheus_cache_evictions_total Total number of times an entry was evicted from query cache due to size limit or TTL.
240# TYPE pint_prometheus_cache_evictions_total counter
241pint_prometheus_cache_evictions_total{name="prom"} 0
242# HELP pint_prometheus_cache_hits_total Total number of query cache hits.
243# TYPE pint_prometheus_cache_hits_total counter
244pint_prometheus_cache_hits_total{endpoint="/foo/0",name="prom"} 20
245pint_prometheus_cache_hits_total{endpoint="/foo/1",name="prom"} 20
246pint_prometheus_cache_hits_total{endpoint="/foo/2",name="prom"} 20
247pint_prometheus_cache_hits_total{endpoint="/foo/3",name="prom"} 20
248pint_prometheus_cache_hits_total{endpoint="/foo/4",name="prom"} 20
249pint_prometheus_cache_hits_total{endpoint="/foo/5",name="prom"} 20
250pint_prometheus_cache_hits_total{endpoint="/foo/6",name="prom"} 20
251pint_prometheus_cache_hits_total{endpoint="/foo/7",name="prom"} 20
252pint_prometheus_cache_hits_total{endpoint="/foo/8",name="prom"} 20
253pint_prometheus_cache_hits_total{endpoint="/foo/9",name="prom"} 20
254# HELP pint_prometheus_cache_miss_total Total number of query cache misses.
255# TYPE pint_prometheus_cache_miss_total counter
256pint_prometheus_cache_miss_total{endpoint="/foo/0",name="prom"} 22
257pint_prometheus_cache_miss_total{endpoint="/foo/1",name="prom"} 22
258pint_prometheus_cache_miss_total{endpoint="/foo/2",name="prom"} 22
259pint_prometheus_cache_miss_total{endpoint="/foo/3",name="prom"} 22
260pint_prometheus_cache_miss_total{endpoint="/foo/4",name="prom"} 22
261pint_prometheus_cache_miss_total{endpoint="/foo/5",name="prom"} 22
262pint_prometheus_cache_miss_total{endpoint="/foo/6",name="prom"} 22
263pint_prometheus_cache_miss_total{endpoint="/foo/7",name="prom"} 22
264pint_prometheus_cache_miss_total{endpoint="/foo/8",name="prom"} 22
265pint_prometheus_cache_miss_total{endpoint="/foo/9",name="prom"} 22
266# HELP pint_prometheus_cache_size Total number of entries currently stored in Prometheus query cache.
267# TYPE pint_prometheus_cache_size gauge
268pint_prometheus_cache_size{name="prom"} 110
269# HELP pint_prometheus_cache_capacity The capacity of Prometheus query cache.
270# TYPE pint_prometheus_cache_capacity gauge
271pint_prometheus_cache_capacity{name="prom"} 110
272`),
273 names...,
274 ))
275}
276
277func BenchmarkQueryCacheOnlySet(b *testing.B) {
278 mockErr := errors.New("Fake Error")
279 cache := newQueryCache(time.Minute, time.Now)
280
281 b.ResetTimer()
282 for b.Loop() {
283 cache.set(1, mockErr, 0)
284 }
285}
286
287func BenchmarkQueryCacheSetGrow(b *testing.B) {
288 const maxSize = 1000
289 mockErr := errors.New("Fake Error")
290 cache := newQueryCache(time.Minute, time.Now)
291
292 var i uint64
293 for i = 1; i <= maxSize; i++ {
294 cache.set(i, mockErr, 0)
295 }
296
297 b.ResetTimer()
298 for n := 1; n <= b.N; n++ {
299 cache.set(uint64(maxSize+n), mockErr, 0)
300 }
301}
302
303func BenchmarkQueryCacheGetMiss(b *testing.B) {
304 cache := newQueryCache(time.Minute, time.Now)
305
306 b.ResetTimer()
307 for n := 0; b.Loop(); n++ {
308 cache.get(uint64(n), "/foo")
309 }
310}
311
312func BenchmarkQueryCacheGC(b *testing.B) {
313 mockErr := errors.New("Fake Error")
314
315 for _, percent := range []uint64{0, 1, 10, 20, 25, 50, 75, 99, 100} {
316 b.Run(fmt.Sprintf("%d%%", percent), func(b *testing.B) {
317 const size uint64 = 1000
318 for b.Loop() {
319 b.StopTimer()
320 var (
321 now time.Time
322 ttl time.Duration
323 mod uint64
324 )
325 cache := newQueryCache(time.Hour, func() time.Time {
326 return now
327 })
328 if percent > 0 {
329 mod = uint64(float64(size) / (float64(size) * float64(percent) / 100.0))
330 }
331
332 for i := range size {
333 switch {
334 case mod == 0:
335 ttl = 0
336 case percent == 100:
337 ttl = time.Millisecond
338 case i%mod == 0:
339 ttl = time.Millisecond
340 default:
341 ttl = 0
342 }
343 cache.set(i, mockErr, ttl)
344 }
345
346 // Bump capacity by test percent to trigger map re-alloc.
347 cache.capacity += int(float64(size) * float64(percent) / 100.0)
348
349 now = now.Add(time.Second)
350 b.StartTimer()
351 cache.gc()
352 }
353 })
354 }
355}