1 package ccache
2
3 import (
4 "sort"
5 "strconv"
6 "sync/atomic"
7 "testing"
8 "time"
9
10 . "github.com/karlseguin/expect"
11 )
12
13 type LayeredCacheTests struct{}
14
15 func Test_LayeredCache(t *testing.T) {
16 Expectify(new(LayeredCacheTests), t)
17 }
18
19 func (_ *LayeredCacheTests) GetsANonExistantValue() {
20 cache := newLayered()
21 Expect(cache.Get("spice", "flow")).To.Equal(nil)
22 Expect(cache.ItemCount()).To.Equal(0)
23 }
24
25 func (_ *LayeredCacheTests) SetANewValue() {
26 cache := newLayered()
27 cache.Set("spice", "flow", "a value", time.Minute)
28 Expect(cache.Get("spice", "flow").Value()).To.Equal("a value")
29 Expect(cache.Get("spice", "stop")).To.Equal(nil)
30 Expect(cache.ItemCount()).To.Equal(1)
31 }
32
33 func (_ *LayeredCacheTests) SetsMultipleValueWithinTheSameLayer() {
34 cache := newLayered()
35 cache.Set("spice", "flow", "value-a", time.Minute)
36 cache.Set("spice", "must", "value-b", time.Minute)
37 cache.Set("leto", "sister", "ghanima", time.Minute)
38 Expect(cache.Get("spice", "flow").Value()).To.Equal("value-a")
39 Expect(cache.Get("spice", "must").Value()).To.Equal("value-b")
40 Expect(cache.Get("spice", "worm")).To.Equal(nil)
41
42 Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima")
43 Expect(cache.Get("leto", "brother")).To.Equal(nil)
44 Expect(cache.Get("baron", "friend")).To.Equal(nil)
45 Expect(cache.ItemCount()).To.Equal(3)
46 }
47
48 func (_ *LayeredCacheTests) ReplaceDoesNothingIfKeyDoesNotExist() {
49 cache := newLayered()
50 Expect(cache.Replace("spice", "flow", "value-a")).To.Equal(false)
51 Expect(cache.Get("spice", "flow")).To.Equal(nil)
52 }
53
54 func (_ *LayeredCacheTests) ReplaceUpdatesTheValue() {
55 cache := newLayered()
56 cache.Set("spice", "flow", "value-a", time.Minute)
57 Expect(cache.Replace("spice", "flow", "value-b")).To.Equal(true)
58 Expect(cache.Get("spice", "flow").Value().(string)).To.Equal("value-b")
59 Expect(cache.ItemCount()).To.Equal(1)
60
61 }
62
63 func (_ *LayeredCacheTests) DeletesAValue() {
64 cache := newLayered()
65 cache.Set("spice", "flow", "value-a", time.Minute)
66 cache.Set("spice", "must", "value-b", time.Minute)
67 cache.Set("leto", "sister", "ghanima", time.Minute)
68 cache.Delete("spice", "flow")
69 Expect(cache.Get("spice", "flow")).To.Equal(nil)
70 Expect(cache.Get("spice", "must").Value()).To.Equal("value-b")
71 Expect(cache.Get("spice", "worm")).To.Equal(nil)
72 Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima")
73 Expect(cache.ItemCount()).To.Equal(2)
74 }
75
76 func (_ *LayeredCacheTests) DeletesAPrefix() {
77 cache := newLayered()
78 Expect(cache.ItemCount()).To.Equal(0)
79
80 cache.Set("spice", "aaa", "1", time.Minute)
81 cache.Set("spice", "aab", "2", time.Minute)
82 cache.Set("spice", "aac", "3", time.Minute)
83 cache.Set("leto", "aac", "3", time.Minute)
84 cache.Set("spice", "ac", "4", time.Minute)
85 cache.Set("spice", "z5", "7", time.Minute)
86 Expect(cache.ItemCount()).To.Equal(6)
87
88 Expect(cache.DeletePrefix("spice", "9a")).To.Equal(0)
89 Expect(cache.ItemCount()).To.Equal(6)
90
91 Expect(cache.DeletePrefix("spice", "aa")).To.Equal(3)
92 Expect(cache.Get("spice", "aaa")).To.Equal(nil)
93 Expect(cache.Get("spice", "aab")).To.Equal(nil)
94 Expect(cache.Get("spice", "aac")).To.Equal(nil)
95 Expect(cache.Get("spice", "ac").Value()).To.Equal("4")
96 Expect(cache.Get("spice", "z5").Value()).To.Equal("7")
97 Expect(cache.ItemCount()).To.Equal(3)
98 }
99
100 func (_ *LayeredCacheTests) DeletesAFunc() {
101 cache := newLayered()
102 Expect(cache.ItemCount()).To.Equal(0)
103
104 cache.Set("spice", "a", 1, time.Minute)
105 cache.Set("leto", "b", 2, time.Minute)
106 cache.Set("spice", "c", 3, time.Minute)
107 cache.Set("spice", "d", 4, time.Minute)
108 cache.Set("spice", "e", 5, time.Minute)
109 cache.Set("spice", "f", 6, time.Minute)
110 Expect(cache.ItemCount()).To.Equal(6)
111
112 Expect(cache.DeleteFunc("spice", func(key string, item *Item) bool {
113 return false
114 })).To.Equal(0)
115 Expect(cache.ItemCount()).To.Equal(6)
116
117 Expect(cache.DeleteFunc("spice", func(key string, item *Item) bool {
118 return item.Value().(int) < 4
119 })).To.Equal(2)
120 Expect(cache.ItemCount()).To.Equal(4)
121
122 Expect(cache.DeleteFunc("spice", func(key string, item *Item) bool {
123 return key == "d"
124 })).To.Equal(1)
125 Expect(cache.ItemCount()).To.Equal(3)
126
127 }
128
129 func (_ *LayeredCacheTests) OnDeleteCallbackCalled() {
130 onDeleteFnCalled := int32(0)
131 onDeleteFn := func(item *Item) {
132 if item.group == "spice" && item.key == "flow" {
133 atomic.AddInt32(&onDeleteFnCalled, 1)
134 }
135 }
136
137 cache := Layered(Configure().OnDelete(onDeleteFn))
138 cache.Set("spice", "flow", "value-a", time.Minute)
139 cache.Set("spice", "must", "value-b", time.Minute)
140 cache.Set("leto", "sister", "ghanima", time.Minute)
141
142 time.Sleep(time.Millisecond * 10)
143 cache.Delete("spice", "flow")
144 time.Sleep(time.Millisecond * 10)
145
146 Expect(cache.Get("spice", "flow")).To.Equal(nil)
147 Expect(cache.Get("spice", "must").Value()).To.Equal("value-b")
148 Expect(cache.Get("spice", "worm")).To.Equal(nil)
149 Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima")
150
151 Expect(atomic.LoadInt32(&onDeleteFnCalled)).To.Eql(1)
152 }
153
154 func (_ *LayeredCacheTests) DeletesALayer() {
155 cache := newLayered()
156 cache.Set("spice", "flow", "value-a", time.Minute)
157 cache.Set("spice", "must", "value-b", time.Minute)
158 cache.Set("leto", "sister", "ghanima", time.Minute)
159 cache.DeleteAll("spice")
160 Expect(cache.Get("spice", "flow")).To.Equal(nil)
161 Expect(cache.Get("spice", "must")).To.Equal(nil)
162 Expect(cache.Get("spice", "worm")).To.Equal(nil)
163 Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima")
164 }
165
166 func (_ LayeredCacheTests) GCsTheOldestItems() {
167 cache := Layered(Configure().ItemsToPrune(10))
168 cache.Set("xx", "a", 23, time.Minute)
169 for i := 0; i < 500; i++ {
170 cache.Set(strconv.Itoa(i), "a", i, time.Minute)
171 }
172 cache.Set("xx", "b", 9001, time.Minute)
173
174 time.Sleep(time.Millisecond * 10)
175 gcLayeredCache(cache)
176 Expect(cache.Get("xx", "a")).To.Equal(nil)
177 Expect(cache.Get("xx", "b").Value()).To.Equal(9001)
178 Expect(cache.Get("8", "a")).To.Equal(nil)
179 Expect(cache.Get("9", "a").Value()).To.Equal(9)
180 Expect(cache.Get("10", "a").Value()).To.Equal(10)
181 }
182
183 func (_ LayeredCacheTests) PromotedItemsDontGetPruned() {
184 cache := Layered(Configure().ItemsToPrune(10).GetsPerPromote(1))
185 for i := 0; i < 500; i++ {
186 cache.Set(strconv.Itoa(i), "a", i, time.Minute)
187 }
188 time.Sleep(time.Millisecond * 10)
189 cache.Get("9", "a")
190 time.Sleep(time.Millisecond * 10)
191 gcLayeredCache(cache)
192 Expect(cache.Get("9", "a").Value()).To.Equal(9)
193 Expect(cache.Get("10", "a")).To.Equal(nil)
194 Expect(cache.Get("11", "a").Value()).To.Equal(11)
195 }
196
197 func (_ LayeredCacheTests) TrackerDoesNotCleanupHeldInstance() {
198 cache := Layered(Configure().ItemsToPrune(10).Track())
199 item0 := cache.TrackingSet("0", "a", 0, time.Minute)
200 for i := 1; i < 11; i++ {
201 cache.Set(strconv.Itoa(i), "a", i, time.Minute)
202 }
203 item1 := cache.TrackingGet("1", "a")
204 time.Sleep(time.Millisecond * 10)
205 gcLayeredCache(cache)
206 Expect(cache.Get("0", "a").Value()).To.Equal(0)
207 Expect(cache.Get("1", "a").Value()).To.Equal(1)
208 item0.Release()
209 item1.Release()
210 gcLayeredCache(cache)
211 Expect(cache.Get("0", "a")).To.Equal(nil)
212 Expect(cache.Get("1", "a")).To.Equal(nil)
213 }
214
215 func (_ LayeredCacheTests) RemovesOldestItemWhenFull() {
216 cache := Layered(Configure().MaxSize(5).ItemsToPrune(1))
217 cache.Set("xx", "a", 23, time.Minute)
218 for i := 0; i < 7; i++ {
219 cache.Set(strconv.Itoa(i), "a", i, time.Minute)
220 }
221 cache.Set("xx", "b", 9001, time.Minute)
222 time.Sleep(time.Millisecond * 10)
223 Expect(cache.Get("xx", "a")).To.Equal(nil)
224 Expect(cache.Get("0", "a")).To.Equal(nil)
225 Expect(cache.Get("1", "a")).To.Equal(nil)
226 Expect(cache.Get("2", "a")).To.Equal(nil)
227 Expect(cache.Get("3", "a").Value()).To.Equal(3)
228 Expect(cache.Get("xx", "b").Value()).To.Equal(9001)
229 Expect(cache.GetDropped()).To.Equal(4)
230 Expect(cache.GetDropped()).To.Equal(0)
231 }
232
233 func (_ LayeredCacheTests) ResizeOnTheFly() {
234 cache := Layered(Configure().MaxSize(9).ItemsToPrune(1))
235 for i := 0; i < 5; i++ {
236 cache.Set(strconv.Itoa(i), "a", i, time.Minute)
237 }
238 cache.SetMaxSize(3)
239 time.Sleep(time.Millisecond * 10)
240 Expect(cache.GetDropped()).To.Equal(2)
241 Expect(cache.Get("0", "a")).To.Equal(nil)
242 Expect(cache.Get("1", "a")).To.Equal(nil)
243 Expect(cache.Get("2", "a").Value()).To.Equal(2)
244 Expect(cache.Get("3", "a").Value()).To.Equal(3)
245 Expect(cache.Get("4", "a").Value()).To.Equal(4)
246
247 cache.Set("5", "a", 5, time.Minute)
248 time.Sleep(time.Millisecond * 5)
249 Expect(cache.GetDropped()).To.Equal(1)
250 Expect(cache.Get("2", "a")).To.Equal(nil)
251 Expect(cache.Get("3", "a").Value()).To.Equal(3)
252 Expect(cache.Get("4", "a").Value()).To.Equal(4)
253 Expect(cache.Get("5", "a").Value()).To.Equal(5)
254
255 cache.SetMaxSize(10)
256 cache.Set("6", "a", 6, time.Minute)
257 time.Sleep(time.Millisecond * 10)
258 Expect(cache.GetDropped()).To.Equal(0)
259 Expect(cache.Get("3", "a").Value()).To.Equal(3)
260 Expect(cache.Get("4", "a").Value()).To.Equal(4)
261 Expect(cache.Get("5", "a").Value()).To.Equal(5)
262 Expect(cache.Get("6", "a").Value()).To.Equal(6)
263 }
264
265 func (_ LayeredCacheTests) RemovesOldestItemWhenFullBySizer() {
266 cache := Layered(Configure().MaxSize(9).ItemsToPrune(2))
267 for i := 0; i < 7; i++ {
268 cache.Set("pri", strconv.Itoa(i), &SizedItem{i, 2}, time.Minute)
269 }
270 time.Sleep(time.Millisecond * 10)
271 Expect(cache.Get("pri", "0")).To.Equal(nil)
272 Expect(cache.Get("pri", "1")).To.Equal(nil)
273 Expect(cache.Get("pri", "2")).To.Equal(nil)
274 Expect(cache.Get("pri", "3")).To.Equal(nil)
275 Expect(cache.Get("pri", "4").Value().(*SizedItem).id).To.Equal(4)
276 }
277
278 func (_ LayeredCacheTests) SetUpdatesSizeOnDelta() {
279 cache := Layered(Configure())
280 cache.Set("pri", "a", &SizedItem{0, 2}, time.Minute)
281 cache.Set("pri", "b", &SizedItem{0, 3}, time.Minute)
282 time.Sleep(time.Millisecond * 5)
283 checkLayeredSize(cache, 5)
284 cache.Set("pri", "b", &SizedItem{0, 3}, time.Minute)
285 time.Sleep(time.Millisecond * 5)
286 checkLayeredSize(cache, 5)
287 cache.Set("pri", "b", &SizedItem{0, 4}, time.Minute)
288 time.Sleep(time.Millisecond * 5)
289 checkLayeredSize(cache, 6)
290 cache.Set("pri", "b", &SizedItem{0, 2}, time.Minute)
291 cache.Set("sec", "b", &SizedItem{0, 3}, time.Minute)
292 time.Sleep(time.Millisecond * 5)
293 checkLayeredSize(cache, 7)
294 cache.Delete("pri", "b")
295 time.Sleep(time.Millisecond * 10)
296 checkLayeredSize(cache, 5)
297 }
298
299 func (_ LayeredCacheTests) ReplaceDoesNotchangeSizeIfNotSet() {
300 cache := Layered(Configure())
301 cache.Set("pri", "1", &SizedItem{1, 2}, time.Minute)
302 cache.Set("pri", "2", &SizedItem{1, 2}, time.Minute)
303 cache.Set("pri", "3", &SizedItem{1, 2}, time.Minute)
304 cache.Replace("sec", "3", &SizedItem{1, 2})
305 time.Sleep(time.Millisecond * 5)
306 checkLayeredSize(cache, 6)
307 }
308
309 func (_ LayeredCacheTests) ReplaceChangesSize() {
310 cache := Layered(Configure())
311 cache.Set("pri", "1", &SizedItem{1, 2}, time.Minute)
312 cache.Set("pri", "2", &SizedItem{1, 2}, time.Minute)
313
314 cache.Replace("pri", "2", &SizedItem{1, 2})
315 time.Sleep(time.Millisecond * 5)
316 checkLayeredSize(cache, 4)
317
318 cache.Replace("pri", "2", &SizedItem{1, 1})
319 time.Sleep(time.Millisecond * 5)
320 checkLayeredSize(cache, 3)
321
322 cache.Replace("pri", "2", &SizedItem{1, 3})
323 time.Sleep(time.Millisecond * 5)
324 checkLayeredSize(cache, 5)
325 }
326
327 func (_ LayeredCacheTests) EachFunc() {
328 cache := Layered(Configure().MaxSize(3).ItemsToPrune(1))
329 Expect(forEachKeysLayered(cache, "1")).To.Equal([]string{})
330
331 cache.Set("1", "a", 1, time.Minute)
332 Expect(forEachKeysLayered(cache, "1")).To.Equal([]string{"a"})
333
334 cache.Set("1", "b", 2, time.Minute)
335 time.Sleep(time.Millisecond * 10)
336 Expect(forEachKeysLayered(cache, "1")).To.Equal([]string{"a", "b"})
337
338 cache.Set("1", "c", 3, time.Minute)
339 time.Sleep(time.Millisecond * 10)
340 Expect(forEachKeysLayered(cache, "1")).To.Equal([]string{"a", "b", "c"})
341
342 cache.Set("1", "d", 4, time.Minute)
343 time.Sleep(time.Millisecond * 10)
344 Expect(forEachKeysLayered(cache, "1")).To.Equal([]string{"b", "c", "d"})
345
346
347 cache.Set("1", "stop", 5, time.Minute)
348 time.Sleep(time.Millisecond * 10)
349 Expect(forEachKeysLayered(cache, "1")).Not.To.Contain("stop")
350
351 cache.Set("1", "e", 6, time.Minute)
352 time.Sleep(time.Millisecond * 10)
353 Expect(forEachKeysLayered(cache, "1")).Not.To.Contain("stop")
354 }
355
356 func newLayered() *LayeredCache {
357 c := Layered(Configure())
358 c.Clear()
359 return c
360 }
361
362 func checkLayeredSize(cache *LayeredCache, sz int64) {
363 cache.Stop()
364 Expect(cache.size).To.Equal(sz)
365 cache.restart()
366 }
367
368 func gcLayeredCache(cache *LayeredCache) {
369 cache.Stop()
370 cache.gc()
371 cache.restart()
372 }
373
374 func forEachKeysLayered(cache *LayeredCache, primary string) []string {
375 keys := make([]string, 0, 10)
376 cache.ForEachFunc(primary, func(key string, i *Item) bool {
377 if key == "stop" {
378 return false
379 }
380 keys = append(keys, key)
381 return true
382 })
383 sort.Strings(keys)
384 return keys
385 }
386
View as plain text