github.com/levb/mattermost-server@v5.3.1+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 "sync" 13 "time" 14 ) 15 16 // Caching Interface 17 type ObjectCache interface { 18 AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64) 19 AddWithDefaultExpires(key, value interface{}) 20 Purge() 21 Get(key interface{}) (value interface{}, ok bool) 22 Remove(key interface{}) 23 Len() int 24 Name() string 25 GetInvalidateClusterEvent() string 26 } 27 28 // Cache is a thread-safe fixed size LRU cache. 29 type Cache struct { 30 size int 31 evictList *list.List 32 items map[interface{}]*list.Element 33 lock sync.RWMutex 34 name string 35 defaultExpiry int64 36 invalidateClusterEvent string 37 currentGeneration int64 38 len int 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 generation int64 47 } 48 49 // New creates an LRU of the given size 50 func NewLru(size int) *Cache { 51 return &Cache{ 52 size: size, 53 evictList: list.New(), 54 items: make(map[interface{}]*list.Element, size), 55 } 56 } 57 58 func NewLruWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) *Cache { 59 lru := NewLru(size) 60 lru.name = name 61 lru.defaultExpiry = defaultExpiry 62 lru.invalidateClusterEvent = invalidateClusterEvent 63 return lru 64 } 65 66 // Purge is used to completely clear the cache 67 func (c *Cache) Purge() { 68 c.lock.Lock() 69 defer c.lock.Unlock() 70 71 c.len = 0 72 c.currentGeneration++ 73 } 74 75 func (c *Cache) Add(key, value interface{}) { 76 c.AddWithExpiresInSecs(key, value, 0) 77 } 78 79 func (c *Cache) AddWithDefaultExpires(key, value interface{}) { 80 c.AddWithExpiresInSecs(key, value, c.defaultExpiry) 81 } 82 83 func (c *Cache) AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64) { 84 c.lock.Lock() 85 defer c.lock.Unlock() 86 87 if expireAtSecs > 0 { 88 expireAtSecs = (time.Now().UnixNano() / int64(time.Second)) + expireAtSecs 89 } 90 91 // Check for existing item 92 if ent, ok := c.items[key]; ok { 93 c.evictList.MoveToFront(ent) 94 e := ent.Value.(*entry) 95 e.value = value 96 e.expireAtSecs = expireAtSecs 97 if e.generation != c.currentGeneration { 98 e.generation = c.currentGeneration 99 c.len++ 100 } 101 return 102 } 103 104 // Add new item 105 ent := &entry{key, value, expireAtSecs, c.currentGeneration} 106 entry := c.evictList.PushFront(ent) 107 c.items[key] = entry 108 c.len++ 109 110 if c.evictList.Len() > c.size { 111 c.removeElement(c.evictList.Back()) 112 } 113 } 114 115 func (c *Cache) Get(key interface{}) (value interface{}, ok bool) { 116 c.lock.Lock() 117 defer c.lock.Unlock() 118 119 if ent, ok := c.items[key]; ok { 120 e := ent.Value.(*entry) 121 122 if e.generation != c.currentGeneration || (e.expireAtSecs > 0 && (time.Now().UnixNano()/int64(time.Second)) > e.expireAtSecs) { 123 c.removeElement(ent) 124 return nil, false 125 } 126 127 c.evictList.MoveToFront(ent) 128 return ent.Value.(*entry).value, true 129 } 130 131 return nil, false 132 } 133 134 func (c *Cache) Remove(key interface{}) { 135 c.lock.Lock() 136 defer c.lock.Unlock() 137 138 if ent, ok := c.items[key]; ok { 139 c.removeElement(ent) 140 } 141 } 142 143 // Keys returns a slice of the keys in the cache, from oldest to newest. 144 func (c *Cache) Keys() []interface{} { 145 c.lock.RLock() 146 defer c.lock.RUnlock() 147 148 keys := make([]interface{}, c.len) 149 i := 0 150 for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() { 151 e := ent.Value.(*entry) 152 if e.generation == c.currentGeneration { 153 keys[i] = e.key 154 i++ 155 } 156 } 157 158 return keys 159 } 160 161 // Len returns the number of items in the cache. 162 func (c *Cache) Len() int { 163 c.lock.RLock() 164 defer c.lock.RUnlock() 165 return c.len 166 } 167 168 func (c *Cache) Name() string { 169 return c.name 170 } 171 172 func (c *Cache) GetInvalidateClusterEvent() string { 173 return c.invalidateClusterEvent 174 } 175 176 // removeElement is used to remove a given list element from the cache 177 func (c *Cache) removeElement(e *list.Element) { 178 c.evictList.Remove(e) 179 kv := e.Value.(*entry) 180 if kv.generation == c.currentGeneration { 181 c.len-- 182 } 183 delete(c.items, kv.key) 184 }