github.com/psyb0t/mattermost-server@v4.6.1-0.20180125161845-5503a1351abf+incompatible/utils/lru.go (about) 1 // This files was copied/modified from https://github.com/hashicorp/golang-lru 2 // which was (see below) 3 4 // This package provides a simple LRU cache. It is based on the 5 // LRU implementation in groupcache: 6 // https://github.com/golang/groupcache/tree/master/lru 7 8 package utils 9 10 import ( 11 "container/list" 12 "errors" 13 "sync" 14 "time" 15 ) 16 17 // Caching Interface 18 type ObjectCache interface { 19 AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64) bool 20 AddWithDefaultExpires(key, value interface{}) bool 21 Purge() 22 Get(key interface{}) (value interface{}, ok bool) 23 Remove(key interface{}) 24 Len() int 25 Name() string 26 GetInvalidateClusterEvent() string 27 } 28 29 // Cache is a thread-safe fixed size LRU cache. 30 type Cache struct { 31 size int 32 evictList *list.List 33 items map[interface{}]*list.Element 34 lock sync.RWMutex 35 onEvicted func(key interface{}, value interface{}) 36 name string 37 defaultExpiry int64 38 invalidateClusterEvent string 39 } 40 41 // entry is used to hold a value in the evictList 42 type entry struct { 43 key interface{} 44 value interface{} 45 expireAtSecs int64 46 } 47 48 // New creates an LRU of the given size 49 func NewLru(size int) *Cache { 50 cache, _ := NewLruWithEvict(size, nil) 51 return cache 52 } 53 54 func NewLruWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) { 55 if size <= 0 { 56 return nil, errors.New(T("utils.iru.with_evict")) 57 } 58 c := &Cache{ 59 size: size, 60 evictList: list.New(), 61 items: make(map[interface{}]*list.Element, size), 62 onEvicted: onEvicted, 63 } 64 return c, nil 65 } 66 67 func NewLruWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) *Cache { 68 lru := NewLru(size) 69 lru.name = name 70 lru.defaultExpiry = defaultExpiry 71 lru.invalidateClusterEvent = invalidateClusterEvent 72 return lru 73 } 74 75 // Purge is used to completely clear the cache 76 func (c *Cache) Purge() { 77 c.lock.Lock() 78 defer c.lock.Unlock() 79 80 if c.onEvicted != nil { 81 for k, v := range c.items { 82 c.onEvicted(k, v.Value) 83 } 84 } 85 86 c.evictList = list.New() 87 c.items = make(map[interface{}]*list.Element, c.size) 88 } 89 90 func (c *Cache) Add(key, value interface{}) bool { 91 return c.AddWithExpiresInSecs(key, value, 0) 92 } 93 94 func (c *Cache) AddWithDefaultExpires(key, value interface{}) bool { 95 return c.AddWithExpiresInSecs(key, value, c.defaultExpiry) 96 } 97 98 // Add adds a value to the cache. Returns true if an eviction occurred. 99 func (c *Cache) AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64) bool { 100 c.lock.Lock() 101 defer c.lock.Unlock() 102 103 if expireAtSecs > 0 { 104 expireAtSecs = (time.Now().UnixNano() / int64(time.Second)) + expireAtSecs 105 } 106 107 // Check for existing item 108 if ent, ok := c.items[key]; ok { 109 c.evictList.MoveToFront(ent) 110 ent.Value.(*entry).value = value 111 ent.Value.(*entry).expireAtSecs = expireAtSecs 112 return false 113 } 114 115 // Add new item 116 ent := &entry{key, value, expireAtSecs} 117 entry := c.evictList.PushFront(ent) 118 c.items[key] = entry 119 120 evict := c.evictList.Len() > c.size 121 // Verify size not exceeded 122 if evict { 123 c.removeOldest() 124 } 125 return evict 126 } 127 128 // Get looks up a key's value from the cache. 129 func (c *Cache) Get(key interface{}) (value interface{}, ok bool) { 130 c.lock.Lock() 131 defer c.lock.Unlock() 132 133 if ent, ok := c.items[key]; ok { 134 135 if ent.Value.(*entry).expireAtSecs > 0 { 136 if (time.Now().UnixNano() / int64(time.Second)) > ent.Value.(*entry).expireAtSecs { 137 c.removeElement(ent) 138 return nil, false 139 } 140 } 141 142 c.evictList.MoveToFront(ent) 143 return ent.Value.(*entry).value, true 144 } 145 return 146 } 147 148 // Remove removes the provided key from the cache. 149 func (c *Cache) Remove(key interface{}) { 150 c.lock.Lock() 151 defer c.lock.Unlock() 152 153 if ent, ok := c.items[key]; ok { 154 c.removeElement(ent) 155 } 156 } 157 158 // RemoveOldest removes the oldest item from the cache. 159 func (c *Cache) RemoveOldest() { 160 c.lock.Lock() 161 defer c.lock.Unlock() 162 c.removeOldest() 163 } 164 165 // Keys returns a slice of the keys in the cache, from oldest to newest. 166 func (c *Cache) Keys() []interface{} { 167 c.lock.RLock() 168 defer c.lock.RUnlock() 169 170 keys := make([]interface{}, len(c.items)) 171 ent := c.evictList.Back() 172 i := 0 173 for ent != nil { 174 keys[i] = ent.Value.(*entry).key 175 ent = ent.Prev() 176 i++ 177 } 178 179 return keys 180 } 181 182 // Len returns the number of items in the cache. 183 func (c *Cache) Len() int { 184 c.lock.RLock() 185 defer c.lock.RUnlock() 186 return c.evictList.Len() 187 } 188 189 func (c *Cache) Name() string { 190 return c.name 191 } 192 193 func (c *Cache) GetInvalidateClusterEvent() string { 194 return c.invalidateClusterEvent 195 } 196 197 // removeOldest removes the oldest item from the cache. 198 func (c *Cache) removeOldest() { 199 ent := c.evictList.Back() 200 if ent != nil { 201 c.removeElement(ent) 202 } 203 } 204 205 // removeElement is used to remove a given list element from the cache 206 func (c *Cache) removeElement(e *list.Element) { 207 c.evictList.Remove(e) 208 kv := e.Value.(*entry) 209 delete(c.items, kv.key) 210 if c.onEvicted != nil { 211 c.onEvicted(kv.key, kv.value) 212 } 213 }