github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/services/cache/lru/lru.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 // This files was copied/modified from https://github.com/hashicorp/golang-lru 5 // which was (see below) 6 7 // This package provides a simple LRU cache. It is based on the 8 // LRU implementation in groupcache: 9 // https://github.com/golang/groupcache/tree/master/lru 10 11 package lru 12 13 import ( 14 "container/list" 15 "sync" 16 "time" 17 18 "github.com/vnforks/kid/v5/services/cache" 19 ) 20 21 // Cache is a thread-safe fixed size LRU cache. 22 type Cache struct { 23 size int 24 evictList *list.List 25 items map[string]*list.Element 26 lock sync.RWMutex 27 name string 28 defaultExpiry int64 29 invalidateClusterEvent string 30 currentGeneration int64 31 len int 32 } 33 34 // CacheProvider is an implementation of cache.Provider to create a new Lru Cache 35 type CacheProvider struct{} 36 37 // NewCache creates a new lru.Cache with given size. 38 func (c *CacheProvider) NewCache(size int) cache.Cache { 39 return New(size) 40 } 41 42 // NewCacheWithParams creates a new lru.Cache with the given parameters. 43 func (c *CacheProvider) NewCacheWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) cache.Cache { 44 return NewWithParams(size, name, defaultExpiry, invalidateClusterEvent) 45 } 46 47 // Connect opens a new connection to the cache using specific provider parameters. 48 func (c *CacheProvider) Connect() { 49 50 } 51 52 // Close releases any resources used by the cache provider. 53 func (c *CacheProvider) Close() { 54 55 } 56 57 // entry is used to hold a value in the evictList. 58 type entry struct { 59 key string 60 value interface{} 61 expires time.Time 62 generation int64 63 } 64 65 // New creates an LRU of the given size. 66 func New(size int) *Cache { 67 return &Cache{ 68 size: size, 69 evictList: list.New(), 70 items: make(map[string]*list.Element, size), 71 } 72 } 73 74 // NewWithParams creates an LRU with the given parameters. 75 func NewWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) *Cache { 76 lru := New(size) 77 lru.name = name 78 lru.defaultExpiry = defaultExpiry 79 lru.invalidateClusterEvent = invalidateClusterEvent 80 return lru 81 } 82 83 // Purge is used to completely clear the cache. 84 func (c *Cache) Purge() { 85 c.lock.Lock() 86 defer c.lock.Unlock() 87 88 c.len = 0 89 c.currentGeneration++ 90 } 91 92 // Add adds the given key and value to the store without an expiry. 93 func (c *Cache) Add(key string, value interface{}) { 94 c.AddWithExpiresInSecs(key, value, 0) 95 } 96 97 // AddWithDefaultExpires adds the given key and value to the store with the default expiry. 98 func (c *Cache) AddWithDefaultExpires(key string, value interface{}) { 99 c.AddWithExpiresInSecs(key, value, c.defaultExpiry) 100 } 101 102 // AddWithExpiresInSecs adds the given key and value to the cache with the given expiry. 103 func (c *Cache) AddWithExpiresInSecs(key string, value interface{}, expireAtSecs int64) { 104 c.lock.Lock() 105 defer c.lock.Unlock() 106 107 c.add(key, value, time.Duration(expireAtSecs)*time.Second) 108 } 109 110 func (c *Cache) add(key string, value interface{}, ttl time.Duration) { 111 var expires time.Time 112 if ttl > 0 { 113 expires = time.Now().Add(ttl) 114 } 115 116 // Check for existing item, ignoring expiry since we'd update anyway. 117 if ent, ok := c.items[key]; ok { 118 c.evictList.MoveToFront(ent) 119 e := ent.Value.(*entry) 120 e.value = value 121 e.expires = expires 122 if e.generation != c.currentGeneration { 123 e.generation = c.currentGeneration 124 c.len++ 125 } 126 return 127 } 128 129 // Add new item 130 ent := &entry{key, value, expires, c.currentGeneration} 131 entry := c.evictList.PushFront(ent) 132 c.items[key] = entry 133 c.len++ 134 135 if c.evictList.Len() > c.size { 136 c.removeElement(c.evictList.Back()) 137 } 138 } 139 140 // Get returns the value stored in the cache for a key, or nil if no value is present. The ok result indicates whether value was found in the cache. 141 func (c *Cache) Get(key string) (value interface{}, ok bool) { 142 c.lock.Lock() 143 defer c.lock.Unlock() 144 145 return c.getValue(key) 146 } 147 148 func (c *Cache) getValue(key string) (value interface{}, ok bool) { 149 if ent, ok := c.items[key]; ok { 150 e := ent.Value.(*entry) 151 152 if e.generation != c.currentGeneration || (!e.expires.IsZero() && time.Now().After(e.expires)) { 153 c.removeElement(ent) 154 return nil, false 155 } 156 157 c.evictList.MoveToFront(ent) 158 return ent.Value.(*entry).value, true 159 } 160 161 return nil, false 162 } 163 164 // GetOrAdd returns the existing value for the key if present. Otherwise, it stores and returns the given value. The loaded result is true if the value was loaded, false if stored. 165 // This API intentionally deviates from the Add-only variants above for simplicity. We should simplify the entire API in the future. 166 func (c *Cache) GetOrAdd(key string, value interface{}, ttl time.Duration) (actual interface{}, loaded bool) { 167 c.lock.Lock() 168 defer c.lock.Unlock() 169 170 // Check for existing item 171 if actualValue, ok := c.getValue(key); ok { 172 return actualValue, true 173 } 174 175 c.add(key, value, ttl) 176 177 return value, false 178 } 179 180 // Remove deletes the value for a key. 181 func (c *Cache) Remove(key string) { 182 c.lock.Lock() 183 defer c.lock.Unlock() 184 185 if ent, ok := c.items[key]; ok { 186 c.removeElement(ent) 187 } 188 } 189 190 // Keys returns a slice of the keys in the cache, from oldest to newest. 191 func (c *Cache) Keys() []string { 192 c.lock.RLock() 193 defer c.lock.RUnlock() 194 195 keys := make([]string, c.len) 196 i := 0 197 for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() { 198 e := ent.Value.(*entry) 199 if e.generation == c.currentGeneration { 200 keys[i] = e.key 201 i++ 202 } 203 } 204 205 return keys 206 } 207 208 // Len returns the number of items in the cache. 209 func (c *Cache) Len() int { 210 c.lock.RLock() 211 defer c.lock.RUnlock() 212 return c.len 213 } 214 215 // Name identifies this cache instance among others in the system. 216 func (c *Cache) Name() string { 217 return c.name 218 } 219 220 // GetInvalidateClusterEvent returns the cluster event configured when this cache was created. 221 func (c *Cache) GetInvalidateClusterEvent() string { 222 return c.invalidateClusterEvent 223 } 224 225 func (c *Cache) removeElement(e *list.Element) { 226 c.evictList.Remove(e) 227 kv := e.Value.(*entry) 228 if kv.generation == c.currentGeneration { 229 c.len-- 230 } 231 delete(c.items, kv.key) 232 }