github.com/fretkak/mattermost-mattermost-server@v5.11.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 // Cache is a thread-safe fixed size LRU cache. 17 type Cache struct { 18 size int 19 evictList *list.List 20 items map[interface{}]*list.Element 21 lock sync.RWMutex 22 name string 23 defaultExpiry int64 24 invalidateClusterEvent string 25 currentGeneration int64 26 len int 27 } 28 29 // entry is used to hold a value in the evictList. 30 type entry struct { 31 key interface{} 32 value interface{} 33 expires time.Time 34 generation int64 35 } 36 37 // New creates an LRU of the given size. 38 func NewLru(size int) *Cache { 39 return &Cache{ 40 size: size, 41 evictList: list.New(), 42 items: make(map[interface{}]*list.Element, size), 43 } 44 } 45 46 // New creates an LRU with the given parameters. 47 func NewLruWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) *Cache { 48 lru := NewLru(size) 49 lru.name = name 50 lru.defaultExpiry = defaultExpiry 51 lru.invalidateClusterEvent = invalidateClusterEvent 52 return lru 53 } 54 55 // Purge is used to completely clear the cache. 56 func (c *Cache) Purge() { 57 c.lock.Lock() 58 defer c.lock.Unlock() 59 60 c.len = 0 61 c.currentGeneration++ 62 } 63 64 // Add adds the given key and value to the store without an expiry. 65 func (c *Cache) Add(key, value interface{}) { 66 c.AddWithExpiresInSecs(key, value, 0) 67 } 68 69 // Add adds the given key and value to the store with the default expiry. 70 func (c *Cache) AddWithDefaultExpires(key, value interface{}) { 71 c.AddWithExpiresInSecs(key, value, c.defaultExpiry) 72 } 73 74 // AddWithExpiresInSecs adds the given key and value to the cache with the given expiry. 75 func (c *Cache) AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64) { 76 c.lock.Lock() 77 defer c.lock.Unlock() 78 79 c.add(key, value, time.Duration(expireAtSecs)*time.Second) 80 } 81 82 func (c *Cache) add(key, value interface{}, ttl time.Duration) { 83 var expires time.Time 84 if ttl > 0 { 85 expires = time.Now().Add(ttl) 86 } 87 88 // Check for existing item, ignoring expiry since we'd update anyway. 89 if ent, ok := c.items[key]; ok { 90 c.evictList.MoveToFront(ent) 91 e := ent.Value.(*entry) 92 e.value = value 93 e.expires = expires 94 if e.generation != c.currentGeneration { 95 e.generation = c.currentGeneration 96 c.len++ 97 } 98 return 99 } 100 101 // Add new item 102 ent := &entry{key, value, expires, c.currentGeneration} 103 entry := c.evictList.PushFront(ent) 104 c.items[key] = entry 105 c.len++ 106 107 if c.evictList.Len() > c.size { 108 c.removeElement(c.evictList.Back()) 109 } 110 } 111 112 // 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. 113 func (c *Cache) Get(key interface{}) (value interface{}, ok bool) { 114 c.lock.Lock() 115 defer c.lock.Unlock() 116 117 return c.getValue(key) 118 } 119 120 func (c *Cache) getValue(key interface{}) (value interface{}, ok bool) { 121 if ent, ok := c.items[key]; ok { 122 e := ent.Value.(*entry) 123 124 if e.generation != c.currentGeneration || (!e.expires.IsZero() && time.Now().After(e.expires)) { 125 c.removeElement(ent) 126 return nil, false 127 } 128 129 c.evictList.MoveToFront(ent) 130 return ent.Value.(*entry).value, true 131 } 132 133 return nil, false 134 } 135 136 // 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. 137 // This API intentionally deviates from the Add-only variants above for simplicity. We should simplify the entire API in the future. 138 func (c *Cache) GetOrAdd(key, value interface{}, ttl time.Duration) (actual interface{}, loaded bool) { 139 c.lock.Lock() 140 defer c.lock.Unlock() 141 142 // Check for existing item 143 if actualValue, ok := c.getValue(key); ok { 144 return actualValue, true 145 } 146 147 c.add(key, value, ttl) 148 149 return value, false 150 } 151 152 // Remove deletes the value for a key. 153 func (c *Cache) Remove(key interface{}) { 154 c.lock.Lock() 155 defer c.lock.Unlock() 156 157 if ent, ok := c.items[key]; ok { 158 c.removeElement(ent) 159 } 160 } 161 162 // Keys returns a slice of the keys in the cache, from oldest to newest. 163 func (c *Cache) Keys() []interface{} { 164 c.lock.RLock() 165 defer c.lock.RUnlock() 166 167 keys := make([]interface{}, c.len) 168 i := 0 169 for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() { 170 e := ent.Value.(*entry) 171 if e.generation == c.currentGeneration { 172 keys[i] = e.key 173 i++ 174 } 175 } 176 177 return keys 178 } 179 180 // Len returns the number of items in the cache. 181 func (c *Cache) Len() int { 182 c.lock.RLock() 183 defer c.lock.RUnlock() 184 return c.len 185 } 186 187 // Name identifies this cache instance among others in the system. 188 func (c *Cache) Name() string { 189 return c.name 190 } 191 192 // GetInvalidateClusterEvent returns the cluster event configured when this cache was created. 193 func (c *Cache) GetInvalidateClusterEvent() string { 194 return c.invalidateClusterEvent 195 } 196 197 func (c *Cache) removeElement(e *list.Element) { 198 c.evictList.Remove(e) 199 kv := e.Value.(*entry) 200 if kv.generation == c.currentGeneration { 201 c.len-- 202 } 203 delete(c.items, kv.key) 204 }