cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.34.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/promapi/cache_test.go

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