Package list golang-github-renekroon-ttlcache / d1b9b07
New upstream version 2.3.0+ds Sascha Steinbiss 5 months ago
7 changed file(s) with 160 addition(s) and 13 deletion(s). Raw diff Collapse all Expand all
0 # 2.3.0 (February 2021)
1
2 ## API changes:
3
4 * #38: Added func (cache *Cache) SetExpirationReasonCallback(callback ExpireReasonCallback) This wil function will replace SetExpirationCallback(..) in the next major version.
5
6 # 2.2.0 (January 2021)
7
8 ## API changes:
9
10 * #37 : a GetMetrics call is now available for some information on hits/misses etc.
11 * #34 : Errors are now const
12
013 # 2.1.0 (October 2020)
114
215 ## API changes
5252 // all other values are allowed to expire
5353 return true
5454 }
55 expirationCallback := func(key string, value interface{}) {
56 fmt.Printf("This key(%s) has expired\n", key)
55
56 expirationCallback := func(key string, reason ttlcache.EvictionReason, value interface{}) {
57 fmt.Printf("This key(%s) has expired because of %s\n", key, reason)
5758 }
5859
5960 loaderFunction := func(key string) (data interface{}, ttl time.Duration, err error) {
6465 }
6566
6667 cache := ttlcache.NewCache()
67 defer cache.Close()
6868 cache.SetTTL(time.Duration(10 * time.Second))
69 cache.SetExpirationCallback(expirationCallback)
69 cache.SetExpirationReasonCallback(expirationCallback)
7070 cache.SetLoaderFunction(loaderFunction)
7171 cache.SetNewItemCallback(newItemCallback)
7272 cache.SetCheckExpirationCallback(checkExpirationCallback)
73 cache.SetCacheSizeLimit(2)
7374
7475 cache.Set("key", "value")
7576 cache.SetWithTTL("keyWithTTL", "value", 10*time.Second)
8182 if result := cache.Remove("keyNNN"); result == notFound {
8283 fmt.Printf("Not found, %d items left\n", count)
8384 }
85
86 cache.Set("key6", "value")
87 cache.Set("key7", "value")
88 metrics := cache.GetMetrics()
89 fmt.Printf("Total inserted: %d\n", metrics.Inserted)
90
91 cache.Close()
92
8493 }
8594
8695 func getFromNetwork(key string) (string, error) {
88 type CheckExpireCallback func(key string, value interface{}) bool
99
1010 // ExpireCallback is used as a callback on item expiration or when notifying of an item new to the cache
11 // Note that ExpireReasonCallback will be the succesor of this function in the next major release.
1112 type ExpireCallback func(key string, value interface{})
13
14 // ExpireReasonCallback is used as a callback on item expiration with extra information why the item expired.
15 type ExpireReasonCallback func(key string, reason EvictionReason, value interface{})
1216
1317 // LoaderFunction can be supplied to retrieve an item where a cache miss occurs. Supply an item specific ttl or Duration.Zero
1418 type LoaderFunction func(key string) (data interface{}, ttl time.Duration, err error)
2024 items map[string]*item
2125 loaderLock map[string]*sync.Cond
2226 expireCallback ExpireCallback
27 expireReasonCallback ExpireReasonCallback
2328 checkExpireCallback CheckExpireCallback
2429 newItemCallback ExpireCallback
2530 priorityQueue *priorityQueue
3338 metrics Metrics
3439 }
3540
41 // EvictionReason is an enum that explains why an item was evicted
42 type EvictionReason int
43
44 const (
45 // Removed : explicitly removed from cache via API call
46 Removed EvictionReason = iota
47 // EvictedSize : evicted due to exceeding the cache size
48 EvictedSize
49 // Expired : the time to live is zero and therefore the item is removed
50 Expired
51 // Closed : the cache was closed
52 Closed
53 )
54
3655 const (
3756 // ErrClosed is raised when operating on a cache where Close() has already been called.
3857 ErrClosed = constError("cache already closed")
5069 item, exists := cache.items[key]
5170 if !exists || item.expired() {
5271 return nil, false, false
53 } else {
5472 }
5573
5674 if item.ttl >= 0 && (item.ttl > 0 || cache.ttl > 0) {
102120 timer.Stop()
103121 cache.mutex.Lock()
104122 if cache.priorityQueue.Len() > 0 {
105 cache.evictjob()
123 cache.evictjob(Closed)
106124 }
107125 cache.mutex.Unlock()
108126 shutdownFeedback <- struct{}{}
125143 }
126144 }
127145
128 func (cache *Cache) removeItem(item *item) {
129 cache.metrics.Evicted++
146 func (cache *Cache) checkExpirationCallback(item *item, reason EvictionReason) {
130147 if cache.expireCallback != nil {
131148 go cache.expireCallback(item.key, item.data)
132149 }
150 if cache.expireReasonCallback != nil {
151 go cache.expireReasonCallback(item.key, reason, item.data)
152 }
153 }
154
155 func (cache *Cache) removeItem(item *item, reason EvictionReason) {
156 cache.metrics.Evicted++
157 cache.checkExpirationCallback(item, reason)
133158 cache.priorityQueue.remove(item)
134159 delete(cache.items, item.key)
135160
136161 }
137162
138 func (cache *Cache) evictjob() {
163 func (cache *Cache) evictjob(reason EvictionReason) {
139164 // index will only be advanced if the current entry will not be evicted
140165 i := 0
141166 for item := cache.priorityQueue.items[i]; ; item = cache.priorityQueue.items[i] {
142167
143 cache.removeItem(item)
168 cache.removeItem(item, reason)
144169 if cache.priorityQueue.Len() == 0 {
145170 return
146171 }
164189 }
165190 }
166191
167 cache.removeItem(item)
192 cache.removeItem(item, Expired)
168193 if cache.priorityQueue.Len() == 0 {
169194 return
170195 }
209234 item.ttl = ttl
210235 } else {
211236 if cache.sizeLimit != 0 && len(cache.items) >= cache.sizeLimit {
212 cache.removeItem(cache.priorityQueue.items[0])
237 cache.removeItem(cache.priorityQueue.items[0], EvictedSize)
213238 }
214239 item = newItem(key, data, ttl)
215240 cache.items[key] = item
326351 if !exists {
327352 return ErrNotFound
328353 }
329 cache.removeItem(object)
354 cache.removeItem(object, Removed)
330355
331356 return nil
332357 }
360385 // SetExpirationCallback sets a callback that will be called when an item expires
361386 func (cache *Cache) SetExpirationCallback(callback ExpireCallback) {
362387 cache.expireCallback = callback
388 }
389
390 // SetExpirationReasonCallback sets a callback that will be called when an item expires, includes reason of expiry
391 func (cache *Cache) SetExpirationReasonCallback(callback ExpireReasonCallback) {
392 cache.expireReasonCallback = callback
363393 }
364394
365395 // SetCheckExpirationCallback sets a callback that will be called when an item is about to expire
1717
1818 func TestMain(m *testing.M) {
1919 goleak.VerifyTestMain(m)
20 }
21
22 // Issue #38: Feature request: ability to know why an expiry has occurred
23 func TestCache_textExpirationReasons(t *testing.T) {
24 t.Parallel()
25 cache := NewCache()
26
27 var reason EvictionReason
28 var sync = make(chan struct{})
29 expirationReason := func(key string, evReason EvictionReason, value interface{}) {
30 reason = evReason
31 sync <- struct{}{}
32 }
33 cache.SetExpirationReasonCallback(expirationReason)
34
35 cache.SetTTL(time.Millisecond)
36 cache.Set("one", "one")
37 <-sync
38 assert.Equal(t, Expired, reason)
39
40 cache.SetTTL(time.Hour)
41 cache.SetCacheSizeLimit(1)
42 cache.Set("two", "two")
43 cache.Set("twoB", "twoB")
44 <-sync
45 assert.Equal(t, EvictedSize, reason)
46
47 cache.Remove("twoB")
48 <-sync
49 assert.Equal(t, Removed, reason)
50
51 cache.SetTTL(time.Hour)
52 cache.Set("three", "three")
53 cache.Close()
54 <-sync
55 assert.Equal(t, Closed, reason)
56
2057 }
2158
2259 // Issue #37: Cache metrics
0 // Code generated by "enumer -type EvictionReason"; DO NOT EDIT.
1
2 //
3 package ttlcache
4
5 import (
6 "fmt"
7 )
8
9 const _EvictionReasonName = "RemovedEvictedSizeExpiredClosed"
10
11 var _EvictionReasonIndex = [...]uint8{0, 7, 18, 25, 31}
12
13 func (i EvictionReason) String() string {
14 if i < 0 || i >= EvictionReason(len(_EvictionReasonIndex)-1) {
15 return fmt.Sprintf("EvictionReason(%d)", i)
16 }
17 return _EvictionReasonName[_EvictionReasonIndex[i]:_EvictionReasonIndex[i+1]]
18 }
19
20 var _EvictionReasonValues = []EvictionReason{0, 1, 2, 3}
21
22 var _EvictionReasonNameToValueMap = map[string]EvictionReason{
23 _EvictionReasonName[0:7]: 0,
24 _EvictionReasonName[7:18]: 1,
25 _EvictionReasonName[18:25]: 2,
26 _EvictionReasonName[25:31]: 3,
27 }
28
29 // EvictionReasonString retrieves an enum value from the enum constants string name.
30 // Throws an error if the param is not part of the enum.
31 func EvictionReasonString(s string) (EvictionReason, error) {
32 if val, ok := _EvictionReasonNameToValueMap[s]; ok {
33 return val, nil
34 }
35 return 0, fmt.Errorf("%s does not belong to EvictionReason values", s)
36 }
37
38 // EvictionReasonValues returns all values of the enum
39 func EvictionReasonValues() []EvictionReason {
40 return _EvictionReasonValues
41 }
42
43 // IsAEvictionReason returns "true" if the value is listed in the enum definition. "false" otherwise
44 func (i EvictionReason) IsAEvictionReason() bool {
45 for _, v := range _EvictionReasonValues {
46 if i == v {
47 return true
48 }
49 }
50 return false
51 }
22 go 1.15
33
44 require (
5 github.com/alvaroloes/enumer v1.1.2 // indirect
56 github.com/davecgh/go-spew v1.1.1 // indirect
67 github.com/stretchr/testify v1.7.0
78 go.uber.org/goleak v1.1.10
0 github.com/alvaroloes/enumer v1.1.2 h1:5khqHB33TZy1GWCO/lZwcroBFh7u+0j40T83VUbfAMY=
1 github.com/alvaroloes/enumer v1.1.2/go.mod h1:FxrjvuXoDAx9isTJrv4c+T410zFi0DtXIT0m65DJ+Wo=
02 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
13 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
24 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1113 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
1214 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
1315 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
16 github.com/pascaldekloe/name v0.0.0-20180628100202-0fd16699aae1 h1:/I3lTljEEDNYLho3/FUB7iD/oc2cEFgVmbHzV+O0PtU=
17 github.com/pascaldekloe/name v0.0.0-20180628100202-0fd16699aae1/go.mod h1:eD5JxqMiuNYyFNmyY9rkJ/slN8y59oEu4Ei7F8OoKWQ=
1418 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1519 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1620 github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
5458 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
5559 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
5660 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
61 golang.org/x/tools v0.0.0-20190524210228-3d17549cdc6b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
5762 golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E=
5863 golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
5964 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=