-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathjanitor_test.go
129 lines (122 loc) · 3.97 KB
/
janitor_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package gocache
import (
"fmt"
"testing"
"time"
)
func TestCache_StartJanitor(t *testing.T) {
cache := NewCache()
cache.SetWithTTL("1", "1", time.Nanosecond)
if cacheSize := cache.Count(); cacheSize != 1 {
t.Errorf("expected cacheSize to be 1, but was %d", cacheSize)
}
err := cache.StartJanitor()
if err != nil {
t.Fatal(err)
}
defer cache.StopJanitor()
time.Sleep(JanitorMinShiftBackOff * 2)
if cacheSize := cache.Count(); cacheSize != 0 {
t.Errorf("expected cacheSize to be 0, but was %d", cacheSize)
}
}
func TestCache_StartJanitorWhenAlreadyStarted(t *testing.T) {
cache := NewCache()
if err := cache.StartJanitor(); err != nil {
t.Fatal(err)
}
if err := cache.StartJanitor(); err == nil {
t.Fatal("expected StartJanitor to return an error, because the janitor is already started")
}
cache.StopJanitor()
}
func TestCache_StopJanitor(t *testing.T) {
cache := NewCache()
_ = cache.StartJanitor()
if cache.stopJanitor == nil {
t.Error("starting the janitor should've initialized cache.stopJanitor")
}
cache.StopJanitor()
if cache.stopJanitor != nil {
t.Error("stopping the janitor should've set cache.stopJanitor to nil")
}
// Check if stopping the janitor even though it's already stopped causes a panic
cache.StopJanitor()
}
func TestJanitor(t *testing.T) {
cache := NewCache().WithMaxSize(3 * JanitorMaxIterationsPerShift)
defer cache.Clear()
for i := 0; i < 3*JanitorMaxIterationsPerShift; i++ {
if i < JanitorMaxIterationsPerShift && i%2 == 0 {
cache.SetWithTTL(fmt.Sprintf("%d", i), "value", time.Millisecond)
} else {
cache.SetWithTTL(fmt.Sprintf("%d", i), "value", time.Hour)
}
}
cacheSize := cache.Count()
err := cache.StartJanitor()
if err != nil {
t.Fatal(err)
}
defer cache.StopJanitor()
time.Sleep(JanitorMinShiftBackOff * 4)
if cacheSize <= cache.Count() {
t.Error("The janitor should be deleting expired cache entries")
}
cacheSize = cache.Count()
time.Sleep(JanitorMinShiftBackOff * 4)
if cacheSize <= cache.Count() {
t.Error("The janitor should be deleting expired cache entries")
}
cacheSize = cache.Count()
time.Sleep(JanitorMinShiftBackOff * 4)
if cacheSize <= cache.Count() {
t.Error("The janitor should be deleting expired cache entries")
}
}
func TestJanitorIsLoopingProperly(t *testing.T) {
cache := NewCache().WithMaxSize(JanitorMaxIterationsPerShift + 3)
defer cache.Clear()
for i := 0; i < JanitorMaxIterationsPerShift; i++ {
cache.SetWithTTL(fmt.Sprintf("%d", i), "value", time.Hour)
}
cache.SetWithTTL("key-to-expire-1", "value", JanitorMinShiftBackOff*2)
cache.SetWithTTL("key-to-expire-2", "value", JanitorMinShiftBackOff*2)
cache.SetWithTTL("key-to-expire-3", "value", JanitorMinShiftBackOff*2)
err := cache.StartJanitor()
if err != nil {
t.Fatal(err)
}
defer cache.StopJanitor()
if cache.Count() != JanitorMaxIterationsPerShift+3 {
t.Error("The janitor shouldn't have had enough time to remove anything from the cache yet", cache.Count())
}
const timeout = JanitorMinShiftBackOff * 20
threeKeysExpiredWithinOneSecond := false
for start := time.Now(); time.Since(start) < timeout; {
if cache.Stats().ExpiredKeys == 3 {
threeKeysExpiredWithinOneSecond = true
break
}
time.Sleep(JanitorMinShiftBackOff)
}
if !threeKeysExpiredWithinOneSecond {
t.Error("expected 3 keys to expire within 1 second")
}
if cache.Count() != JanitorMaxIterationsPerShift {
t.Error("The janitor should've deleted 3 entries")
}
}
func TestJanitorDoesNotThrowATantrumWhenThereIsNothingToClean(t *testing.T) {
cache := NewCache()
start := time.Now()
_ = cache.StartJanitor()
defer cache.StopJanitor()
time.Sleep(JanitorMaxShiftBackOff * 3)
// Technically, if the janitor doesn't backoff properly, the sleep above is likely to take more time than the sleep
// below because it would be eating up the CPU.
// This is a far-fetched test, but it's a good sanity check.
if time.Since(start) > JanitorMaxShiftBackOff*4 {
t.Error("The janitor should've backed off and prevented CPU usage from throttling the application")
}
}