cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.40.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/cache_test.go

321lines · 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)
17
18 var i uint64
19 for i = 1; i <= 100; i++ {
20 cache.set(i, mockErr, 0, "/foo")
21 }
22
23 require.Equal(t, 100, len(cache.entries))
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)
30
31 cache.set(6, mockErr, 0, "/foo")
32 cache.set(6, mockErr, 0, "/foo")
33 cache.set(6, mockErr, 0, "/foo")
34
35 require.Equal(t, 1, len(cache.entries))
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)
42
43 var i uint64
44 for i = 1; i <= 100; i++ {
45 // first get
46 v, ok := cache.get(i, "/foo")
47 require.Equal(t, false, 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, "/foo")
52
53 // second get, should be in cache now
54 v, ok = cache.get(i, "/foo")
55 require.Equal(t, true, ok, "should be present in cache on third get")
56 require.NotZero(t, v)
57 require.Equal(t, mockErr, v)
58 }
59
60 require.Equal(t, 100, len(cache.entries))
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.Equal(t, 100, len(cache.entries))
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)
76
77 var i uint64
78 for i = 1; i <= maxSize; i++ {
79 cache.set(i, mockErr, 0, "/foo")
80 _, _ = cache.get(i, "/foo")
81 }
82 require.Equal(t, 100, len(cache.entries))
83 require.Equal(t, 0, cache.evictions)
84
85 time.Sleep(time.Second)
86
87 cache.gc()
88 require.Equal(t, 100, len(cache.entries))
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)
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, "/foo")
102 _, _ = cache.get(i, "/foo")
103 }
104 require.Equal(t, 100, len(cache.entries))
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.Equal(t, 50, len(cache.entries))
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)
119
120 var i, j uint64
121 for i = 1; i <= 100; i++ {
122 cache.set(i, mockErr, time.Minute, "/foo")
123 for j = 1; j <= i; j++ {
124 _, _ = cache.get(i, "/foo")
125 }
126 }
127 require.Equal(t, 100, len(cache.entries))
128 require.Equal(t, 0, cache.evictions)
129
130 cache.gc()
131 require.Equal(t, 100, len(cache.entries))
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.Equal(t, 50, len(cache.entries))
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)
155
156 names := []string{
157 "pint_prometheus_cache_size",
158 "pint_prometheus_cache_hits_total",
159 "pint_prometheus_cache_miss_total",
160 "pint_prometheus_cache_evictions_total",
161 }
162
163 collector := newCacheCollector(cache, "prom")
164 require.NoError(t, testutil.CollectAndCompare(
165 collector, strings.NewReader(`
166# HELP pint_prometheus_cache_evictions_total Total number of times an entry was evicted from query cache due to size limit or TTL
167# TYPE pint_prometheus_cache_evictions_total counter
168pint_prometheus_cache_evictions_total{name="prom"} 0
169# HELP pint_prometheus_cache_size Total number of entries currently stored in Prometheus query cache
170# TYPE pint_prometheus_cache_size gauge
171pint_prometheus_cache_size{name="prom"} 0
172`),
173 names...,
174 ))
175
176 var i uint64
177 for i = 1; i <= 100; i++ {
178 endpoint := fmt.Sprintf("/foo/%d", i%10)
179 _, _ = cache.get(i, endpoint)
180 _, _ = cache.get(i, endpoint)
181 cache.set(i, queryResult{}, time.Minute, endpoint)
182 _, _ = cache.get(i, endpoint)
183 cache.set(i, queryResult{}, time.Minute, endpoint)
184 _, _ = cache.get(i, endpoint)
185 }
186
187 require.NoError(t, testutil.CollectAndCompare(
188 collector, strings.NewReader(`
189# HELP pint_prometheus_cache_evictions_total Total number of times an entry was evicted from query cache due to size limit or TTL
190# TYPE pint_prometheus_cache_evictions_total counter
191pint_prometheus_cache_evictions_total{name="prom"} 0
192# HELP pint_prometheus_cache_hits_total Total number of query cache hits
193# TYPE pint_prometheus_cache_hits_total counter
194pint_prometheus_cache_hits_total{endpoint="/foo/0",name="prom"} 20
195pint_prometheus_cache_hits_total{endpoint="/foo/1",name="prom"} 20
196pint_prometheus_cache_hits_total{endpoint="/foo/2",name="prom"} 20
197pint_prometheus_cache_hits_total{endpoint="/foo/3",name="prom"} 20
198pint_prometheus_cache_hits_total{endpoint="/foo/4",name="prom"} 20
199pint_prometheus_cache_hits_total{endpoint="/foo/5",name="prom"} 20
200pint_prometheus_cache_hits_total{endpoint="/foo/6",name="prom"} 20
201pint_prometheus_cache_hits_total{endpoint="/foo/7",name="prom"} 20
202pint_prometheus_cache_hits_total{endpoint="/foo/8",name="prom"} 20
203pint_prometheus_cache_hits_total{endpoint="/foo/9",name="prom"} 20
204# HELP pint_prometheus_cache_miss_total Total number of query cache misses
205# TYPE pint_prometheus_cache_miss_total counter
206pint_prometheus_cache_miss_total{endpoint="/foo/0",name="prom"} 20
207pint_prometheus_cache_miss_total{endpoint="/foo/1",name="prom"} 20
208pint_prometheus_cache_miss_total{endpoint="/foo/2",name="prom"} 20
209pint_prometheus_cache_miss_total{endpoint="/foo/3",name="prom"} 20
210pint_prometheus_cache_miss_total{endpoint="/foo/4",name="prom"} 20
211pint_prometheus_cache_miss_total{endpoint="/foo/5",name="prom"} 20
212pint_prometheus_cache_miss_total{endpoint="/foo/6",name="prom"} 20
213pint_prometheus_cache_miss_total{endpoint="/foo/7",name="prom"} 20
214pint_prometheus_cache_miss_total{endpoint="/foo/8",name="prom"} 20
215pint_prometheus_cache_miss_total{endpoint="/foo/9",name="prom"} 20
216# HELP pint_prometheus_cache_size Total number of entries currently stored in Prometheus query cache
217# TYPE pint_prometheus_cache_size gauge
218pint_prometheus_cache_size{name="prom"} 100
219`),
220 names...,
221 ))
222
223 for i = 101; i <= 110; i++ {
224 endpoint := fmt.Sprintf("/foo/%d", i%10)
225 _, _ = cache.get(i, endpoint)
226 _, _ = cache.get(i, endpoint)
227 cache.set(i, queryResult{}, time.Minute, endpoint)
228 }
229
230 require.NoError(t, testutil.CollectAndCompare(
231 collector, strings.NewReader(`
232# HELP pint_prometheus_cache_evictions_total Total number of times an entry was evicted from query cache due to size limit or TTL
233# TYPE pint_prometheus_cache_evictions_total counter
234pint_prometheus_cache_evictions_total{name="prom"} 0
235# HELP pint_prometheus_cache_hits_total Total number of query cache hits
236# TYPE pint_prometheus_cache_hits_total counter
237pint_prometheus_cache_hits_total{endpoint="/foo/0",name="prom"} 20
238pint_prometheus_cache_hits_total{endpoint="/foo/1",name="prom"} 20
239pint_prometheus_cache_hits_total{endpoint="/foo/2",name="prom"} 20
240pint_prometheus_cache_hits_total{endpoint="/foo/3",name="prom"} 20
241pint_prometheus_cache_hits_total{endpoint="/foo/4",name="prom"} 20
242pint_prometheus_cache_hits_total{endpoint="/foo/5",name="prom"} 20
243pint_prometheus_cache_hits_total{endpoint="/foo/6",name="prom"} 20
244pint_prometheus_cache_hits_total{endpoint="/foo/7",name="prom"} 20
245pint_prometheus_cache_hits_total{endpoint="/foo/8",name="prom"} 20
246pint_prometheus_cache_hits_total{endpoint="/foo/9",name="prom"} 20
247# HELP pint_prometheus_cache_miss_total Total number of query cache misses
248# TYPE pint_prometheus_cache_miss_total counter
249pint_prometheus_cache_miss_total{endpoint="/foo/0",name="prom"} 22
250pint_prometheus_cache_miss_total{endpoint="/foo/1",name="prom"} 22
251pint_prometheus_cache_miss_total{endpoint="/foo/2",name="prom"} 22
252pint_prometheus_cache_miss_total{endpoint="/foo/3",name="prom"} 22
253pint_prometheus_cache_miss_total{endpoint="/foo/4",name="prom"} 22
254pint_prometheus_cache_miss_total{endpoint="/foo/5",name="prom"} 22
255pint_prometheus_cache_miss_total{endpoint="/foo/6",name="prom"} 22
256pint_prometheus_cache_miss_total{endpoint="/foo/7",name="prom"} 22
257pint_prometheus_cache_miss_total{endpoint="/foo/8",name="prom"} 22
258pint_prometheus_cache_miss_total{endpoint="/foo/9",name="prom"} 22
259# HELP pint_prometheus_cache_size Total number of entries currently stored in Prometheus query cache
260# TYPE pint_prometheus_cache_size gauge
261pint_prometheus_cache_size{name="prom"} 110
262`),
263 names...,
264 ))
265}
266
267func BenchmarkQueryCacheOnlySet(b *testing.B) {
268 mockErr := errors.New("Fake Error")
269 cache := newQueryCache(time.Minute)
270
271 endpoint := "/foo"
272 for n := 0; n < b.N; n++ {
273 cache.set(1, mockErr, 0, endpoint)
274 }
275}
276
277func BenchmarkQueryCacheSetGrow(b *testing.B) {
278 const maxSize = 1000
279 mockErr := errors.New("Fake Error")
280 cache := newQueryCache(time.Minute)
281
282 var i uint64
283 for i = 1; i <= maxSize; i++ {
284 cache.set(i, mockErr, 0, "/foo")
285 }
286
287 endpoint := "/foo"
288 for n := 1; n <= b.N; n++ {
289 cache.set(uint64(maxSize+n), mockErr, 0, endpoint)
290 }
291}
292
293func BenchmarkQueryCacheGetMiss(b *testing.B) {
294 cache := newQueryCache(time.Minute)
295
296 for n := 0; n < b.N; n++ {
297 cache.get(uint64(n), "/foo")
298 }
299}
300
301func BenchmarkQueryCacheGC(b *testing.B) {
302 mockErr := errors.New("Fake Error")
303 cache := newQueryCache(time.Minute)
304
305 var i uint64
306 var ttl time.Duration
307 for n := 0; n < b.N; n++ {
308 b.StopTimer()
309 if n%2 == 0 {
310 ttl = 0
311 } else {
312 ttl = time.Millisecond
313 }
314 for i = 1; i <= 1000; i++ {
315 cache.set(i, mockErr, ttl, "/foo")
316 }
317 time.Sleep(time.Millisecond * 2)
318 b.StartTimer()
319 cache.gc()
320 }
321}
322