Update upstream source from tag 'upstream/1.7.0+ds'
Update to upstream version '1.7.0+ds'
with Debian dir 22791fe1c2089eddae5bb9b561a8fda05b3883eb
Sascha Steinbiss
3 years ago
0 | 0 | language: go |
1 | 1 | |
2 | 2 | go: |
3 | - 1.13 | |
4 | - 1.12 | |
3 | - "1.14" | |
4 | - "1.13" | |
5 | 5 | git: |
6 | 6 | depth: 1 |
7 | 7 | |
8 | 8 | install: |
9 | 9 | - go install -race std |
10 | - go get golang.org/x/tools/cmd/cover | |
11 | - go get golang.org/x/lint/golint | |
10 | - go install golang.org/x/tools/cmd/cover | |
11 | - go install golang.org/x/lint/golint | |
12 | 12 | - export PATH=$HOME/gopath/bin:$PATH |
13 | 13 | |
14 | 14 | script: |
7 | 7 | 4. Fast and memory efficient |
8 | 8 | 5. Can trigger callback on key expiration |
9 | 9 | 6. Cleanup resources by calling `Close()` at end of lifecycle. |
10 | ||
11 | Note (issue #25): by default, due to historic reasons, the TTL will be reset on each cache hit and you need to explicitly configure the cache to use a TTL that will not get extended. | |
10 | 12 | |
11 | 13 | [](https://travis-ci.org/ReneKroon/ttlcache) |
12 | 14 | |
37 | 39 | } |
38 | 40 | |
39 | 41 | cache := ttlcache.NewCache() |
40 | defer ttlcache.Close() | |
42 | defer cache.Close() | |
41 | 43 | cache.SetTTL(time.Duration(10 * time.Second)) |
42 | 44 | cache.SetExpirationCallback(expirationCallback) |
43 | 45 | |
62 | 64 | The main differences are: |
63 | 65 | |
64 | 66 | 1. A item can store any kind of object, previously, only strings could be saved |
65 | 2. Optionally, you can add callbacks to: check if a value should expire, be notified if a value expires, and be notified when new values are added to the cache | |
67 | 2. Optionally, you can add callbacks too: check if a value should expire, be notified if a value expires, and be notified when new values are added to the cache | |
66 | 68 | 3. The expiration can be either global or per item |
67 | 69 | 4. Can exist items without expiration time |
68 | 70 | 5. Expirations and callbacks are realtime. Don't have a pooling time to check anymore, now it's done with a heap. |
79 | 79 | select { |
80 | 80 | case shutdownFeedback := <-cache.shutdownSignal: |
81 | 81 | timer.Stop() |
82 | cache.mutex.Lock() | |
83 | if cache.priorityQueue.Len() > 0 { | |
84 | cache.evictjob() | |
85 | } | |
86 | cache.mutex.Unlock() | |
82 | 87 | shutdownFeedback <- struct{}{} |
83 | 88 | return |
84 | 89 | case <-timer.C: |
89 | 94 | continue |
90 | 95 | } |
91 | 96 | |
92 | // index will only be advanced if the current entry will not be evicted | |
93 | i := 0 | |
94 | for item := cache.priorityQueue.items[i]; item.expired(); item = cache.priorityQueue.items[i] { | |
95 | ||
96 | if cache.checkExpireCallback != nil { | |
97 | if !cache.checkExpireCallback(item.key, item.data) { | |
98 | item.touch() | |
99 | cache.priorityQueue.update(item) | |
100 | i++ | |
101 | if i == cache.priorityQueue.Len() { | |
102 | break | |
103 | } | |
104 | continue | |
105 | } | |
106 | } | |
107 | ||
108 | cache.priorityQueue.remove(item) | |
109 | delete(cache.items, item.key) | |
110 | if cache.expireCallback != nil { | |
111 | go cache.expireCallback(item.key, item.data) | |
112 | } | |
113 | if cache.priorityQueue.Len() == 0 { | |
114 | goto done | |
115 | } | |
116 | } | |
117 | done: | |
97 | cache.cleanjob() | |
118 | 98 | cache.mutex.Unlock() |
119 | 99 | |
120 | 100 | case <-cache.expirationNotification: |
121 | 101 | timer.Stop() |
122 | 102 | continue |
103 | } | |
104 | } | |
105 | } | |
106 | ||
107 | func (cache *Cache) evictjob() { | |
108 | // index will only be advanced if the current entry will not be evicted | |
109 | i := 0 | |
110 | for item := cache.priorityQueue.items[i]; ; item = cache.priorityQueue.items[i] { | |
111 | ||
112 | cache.priorityQueue.remove(item) | |
113 | delete(cache.items, item.key) | |
114 | if cache.expireCallback != nil { | |
115 | go cache.expireCallback(item.key, item.data) | |
116 | } | |
117 | if cache.priorityQueue.Len() == 0 { | |
118 | return | |
119 | } | |
120 | } | |
121 | } | |
122 | ||
123 | func (cache *Cache) cleanjob() { | |
124 | // index will only be advanced if the current entry will not be evicted | |
125 | i := 0 | |
126 | for item := cache.priorityQueue.items[i]; item.expired(); item = cache.priorityQueue.items[i] { | |
127 | ||
128 | if cache.checkExpireCallback != nil { | |
129 | if !cache.checkExpireCallback(item.key, item.data) { | |
130 | item.touch() | |
131 | cache.priorityQueue.update(item) | |
132 | i++ | |
133 | if i == cache.priorityQueue.Len() { | |
134 | break | |
135 | } | |
136 | continue | |
137 | } | |
138 | } | |
139 | ||
140 | cache.priorityQueue.remove(item) | |
141 | delete(cache.items, item.key) | |
142 | if cache.expireCallback != nil { | |
143 | go cache.expireCallback(item.key, item.data) | |
144 | } | |
145 | if cache.priorityQueue.Len() == 0 { | |
146 | return | |
123 | 147 | } |
124 | 148 | } |
125 | 149 | } |
15 | 15 | func TestMain(m *testing.M) { |
16 | 16 | goleak.VerifyTestMain(m) |
17 | 17 | } |
18 | ||
19 | // Issue #28: call expirationCallback automatically on cache.Close() | |
20 | func TestCache_ExpirationOnClose(t *testing.T) { | |
21 | ||
22 | cache := NewCache() | |
23 | ||
24 | success := make(chan struct{}) | |
25 | defer close(success) | |
26 | ||
27 | cache.SetTTL(time.Hour * 100) | |
28 | cache.SetExpirationCallback(func(key string, value interface{}) { | |
29 | t.Logf("%s\t%v", key, value) | |
30 | success <- struct{}{} | |
31 | }) | |
32 | cache.Set("1", 1) | |
33 | cache.Set("2", 1) | |
34 | cache.Set("3", 1) | |
35 | ||
36 | found := 0 | |
37 | cache.Close() | |
38 | wait := time.NewTimer(time.Millisecond * 100) | |
39 | for found != 3 { | |
40 | select { | |
41 | case <-success: | |
42 | found++ | |
43 | case <-wait.C: | |
44 | t.Fail() | |
45 | } | |
46 | } | |
47 | ||
48 | } | |
49 | ||
50 | // # Issue 29: After Close() the behaviour of Get, Set, Remove is not defined. | |
51 | /* | |
52 | func TestCache_ModifyAfterClose(t *testing.T) { | |
53 | cache := NewCache() | |
54 | ||
55 | cache.SetTTL(time.Hour * 100) | |
56 | cache.SetExpirationCallback(func(key string, value interface{}) { | |
57 | t.Logf("%s\t%v", key, value) | |
58 | }) | |
59 | cache.Set("1", 1) | |
60 | cache.Set("2", 1) | |
61 | cache.Set("3", 1) | |
62 | ||
63 | cache.Close() | |
64 | ||
65 | cache.Get("broken3") | |
66 | cache.Set("broken", 1) | |
67 | cache.Remove("broken2") | |
68 | ||
69 | wait := time.NewTimer(time.Millisecond * 100) | |
70 | ||
71 | select { | |
72 | case <-wait.C: | |
73 | t.Fail() | |
74 | } | |
75 | ||
76 | }*/ | |
18 | 77 | |
19 | 78 | // Issue #23: Goroutine leak on closing. When adding a close method i would like to see |
20 | 79 | // that it can be called in a repeated way without problems. |
0 | 0 | module github.com/ReneKroon/ttlcache |
1 | 1 | |
2 | go 1.12 | |
2 | go 1.14 | |
3 | 3 | |
4 | 4 | require ( |
5 | 5 | github.com/davecgh/go-spew v1.1.1 // indirect |
2 | 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
3 | 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
4 | 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
5 | github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= | |
5 | 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
6 | 7 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= |
7 | 8 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= |