Codebase list golang-github-renekroon-ttlcache / upstream/1.5.0
New upstream version 1.5.0 Sascha Steinbiss 4 years ago
5 changed file(s) with 151 addition(s) and 40 deletion(s). Raw diff Collapse all Expand all
99 - go install -race std
1010 - go get golang.org/x/tools/cmd/cover
1111 - go get golang.org/x/lint/golint
12 - go get github.com/tools/godep
1312 - export PATH=$HOME/gopath/bin:$PATH
14 - godep restore
1513
1614 script:
1715 - golint .
0 # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
1
2
3 [[projects]]
4 digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
5 name = "github.com/davecgh/go-spew"
6 packages = ["spew"]
7 pruneopts = "UT"
8 revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
9 version = "v1.1.1"
10
11 [[projects]]
12 digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
13 name = "github.com/pmezard/go-difflib"
14 packages = ["difflib"]
15 pruneopts = "UT"
16 revision = "792786c7400a136282c1664665ae0a8db921c6c2"
17 version = "v1.0.0"
18
19 [[projects]]
20 digest = "1:972c2427413d41a1e06ca4897e8528e5a1622894050e2f527b38ddf0f343f759"
21 name = "github.com/stretchr/testify"
22 packages = ["assert"]
23 pruneopts = "UT"
24 revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
25 version = "v1.3.0"
26
27 [solve-meta]
28 analyzer-name = "dep"
29 analyzer-version = 1
30 input-imports = ["github.com/stretchr/testify/assert"]
31 solver-name = "gps-cdcl"
32 solver-version = 1
0 # Gopkg.toml example
1 #
2 # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
3 # for detailed Gopkg.toml documentation.
4 #
5 # required = ["github.com/user/thing/cmd/thing"]
6 # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
7 #
8 # [[constraint]]
9 # name = "github.com/user/project"
10 # version = "1.0.0"
11 #
12 # [[constraint]]
13 # name = "github.com/user/project2"
14 # branch = "dev"
15 # source = "github.com/myfork/project2"
16 #
17 # [[override]]
18 # name = "github.com/x/y"
19 # version = "2.4.0"
20 #
21 # [prune]
22 # non-go = false
23 # go-tests = true
24 # unused-packages = true
25
26
27 [[constraint]]
28 name = "github.com/stretchr/testify"
29 version = "1.3.0"
30
31 [prune]
32 go-tests = true
33 unused-packages = true
1212
1313 // Cache is a synchronized map of items that can auto-expire once stale
1414 type Cache struct {
15 mutex sync.RWMutex
15 mutex sync.Mutex
1616 ttl time.Duration
1717 items map[string]*item
1818 expireCallback expireCallback
2424 skipTTLExtension bool
2525 }
2626
27 func (cache *Cache) getItem(key string) (*item, bool) {
28 cache.mutex.RLock()
29
27 func (cache *Cache) getItem(key string) (*item, bool, bool) {
3028 item, exists := cache.items[key]
3129 if !exists || item.expired() {
32 cache.mutex.RUnlock()
33 return nil, false
30 return nil, false, false
3431 }
3532
3633 if item.ttl >= 0 && (item.ttl > 0 || cache.ttl > 0) {
4441 cache.priorityQueue.update(item)
4542 }
4643
47 cache.mutex.RUnlock()
48 cache.expirationNotificationTrigger(item)
49 return item, exists
44 expirationNotification := false
45 if cache.expirationTime.After(time.Now().Add(item.ttl)) {
46 expirationNotification = true
47 }
48 return item, exists, expirationNotification
5049 }
5150
5251 func (cache *Cache) startExpirationProcessing() {
52 timer := time.NewTimer(time.Hour)
5353 for {
5454 var sleepTime time.Duration
5555 cache.mutex.Lock()
7373 cache.expirationTime = time.Now().Add(sleepTime)
7474 cache.mutex.Unlock()
7575
76 timer := time.NewTimer(sleepTime)
76 timer.Reset(sleepTime)
7777 select {
7878 case <-timer.C:
7979 timer.Stop()
118118 }
119119 }
120120
121 func (cache *Cache) expirationNotificationTrigger(item *item) {
122 cache.mutex.Lock()
123 if cache.expirationTime.After(time.Now().Add(item.ttl)) {
124 cache.mutex.Unlock()
125 cache.expirationNotification <- true
126 } else {
127 cache.mutex.Unlock()
128 }
129 }
130
131121 // Set is a thread-safe way to add new items to the map
132122 func (cache *Cache) Set(key string, data interface{}) {
133123 cache.SetWithTTL(key, data, ItemExpireWithGlobalTTL)
135125
136126 // SetWithTTL is a thread-safe way to add new items to the map with individual ttl
137127 func (cache *Cache) SetWithTTL(key string, data interface{}, ttl time.Duration) {
138 item, exists := cache.getItem(key)
139 cache.mutex.Lock()
128 cache.mutex.Lock()
129 item, exists, _ := cache.getItem(key)
140130
141131 if exists {
142132 item.data = data
169159 // Get is a thread-safe way to lookup items
170160 // Every lookup, also touches the item, hence extending it's life
171161 func (cache *Cache) Get(key string) (interface{}, bool) {
172 item, exists := cache.getItem(key)
162 cache.mutex.Lock()
163 item, exists, triggerExpirationNotification := cache.getItem(key)
164
165 var dataToReturn interface{}
173166 if exists {
174 cache.mutex.RLock()
175 defer cache.mutex.RUnlock()
176 return item.data, true
177 }
178 return nil, false
167 dataToReturn = item.data
168 }
169 cache.mutex.Unlock()
170 if triggerExpirationNotification {
171 cache.expirationNotification <- true
172 }
173 return dataToReturn, exists
179174 }
180175
181176 func (cache *Cache) Remove(key string) bool {
194189
195190 // Count returns the number of items in the cache
196191 func (cache *Cache) Count() int {
197 cache.mutex.RLock()
192 cache.mutex.Lock()
198193 length := len(cache.items)
199 cache.mutex.RUnlock()
194 cache.mutex.Unlock()
200195 return length
201196 }
202197
00 package ttlcache
11
22 import (
3 "math/rand"
34 "testing"
45 "time"
56
3334 }
3435 }
3536
36 func TestCache_SkipTtlExtensionOnHit_ForRacesAcrossGoroutines(t *testing.T) {
37 func TestCache_ForRacesAcrossGoroutines(t *testing.T) {
3738 cache := NewCache()
3839 cache.SetTTL(time.Minute * 1)
39 cache.SkipTtlExtensionOnHit(true)
40 cache.SkipTtlExtensionOnHit(false)
4041
4142 var wgSet sync.WaitGroup
4243 var wgGet sync.WaitGroup
4748 for i := 0; i < n; i++ {
4849 wgSet.Add(1)
4950
50 go func() {
51 cache.Set("test", false)
51 go func(i int) {
52 time.Sleep(time.Nanosecond * time.Duration(rand.Int63n(1000000)))
53 if i%2 == 0 {
54 cache.Set(fmt.Sprintf("test%d", i /10), false)
55 } else {
56 cache.SetWithTTL(fmt.Sprintf("test%d", i /10), false, time.Second*59)
57 }
5258 wgSet.Done()
53 }()
59 }(i)
5460 }
5561 wgSet.Done()
5662 }()
5965 for i := 0; i < n; i++ {
6066 wgGet.Add(1)
6167
62 go func() {
63 cache.Get("test")
68 go func(i int) {
69 time.Sleep(time.Nanosecond * time.Duration(rand.Int63n(1000000)))
70 cache.Get(fmt.Sprintf("test%d", i /10))
6471 wgGet.Done()
65 }()
72 }(i)
73 }
74 wgGet.Done()
75 }()
76
77 wgGet.Wait()
78 wgSet.Wait()
79 }
80
81 func TestCache_SkipTtlExtensionOnHit_ForRacesAcrossGoroutines(t *testing.T) {
82 cache := NewCache()
83 cache.SetTTL(time.Minute * 1)
84 cache.SkipTtlExtensionOnHit(true)
85
86 var wgSet sync.WaitGroup
87 var wgGet sync.WaitGroup
88
89 n := 500
90 wgSet.Add(1)
91 go func() {
92 for i := 0; i < n; i++ {
93 wgSet.Add(1)
94
95 go func(i int) {
96 time.Sleep(time.Nanosecond * time.Duration(rand.Int63n(1000000)))
97 if i%2 == 0 {
98 cache.Set(fmt.Sprintf("test%d", i /10), false)
99 } else {
100 cache.SetWithTTL(fmt.Sprintf("test%d", i /10), false, time.Second*59)
101 }
102 wgSet.Done()
103 }(i)
104 }
105 wgSet.Done()
106 }()
107 wgGet.Add(1)
108 go func() {
109 for i := 0; i < n; i++ {
110 wgGet.Add(1)
111
112 go func(i int) {
113 time.Sleep(time.Nanosecond * time.Duration(rand.Int63n(1000000)))
114 cache.Get(fmt.Sprintf("test%d", i /10))
115 wgGet.Done()
116 }(i)
66117 }
67118 wgGet.Done()
68119 }()