cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.33.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/cache_test.go

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