github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/services/cache/lru.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package cache 5 6 import ( 7 "container/list" 8 "sync" 9 "time" 10 11 "github.com/tinylib/msgp/msgp" 12 "github.com/vmihailenco/msgpack/v5" 13 14 "github.com/masterhung0112/hk_server/v5/model" 15 ) 16 17 // LRU is a thread-safe fixed size LRU cache. 18 type LRU struct { 19 lock sync.RWMutex 20 size int 21 len int 22 currentGeneration int64 23 evictList *list.List 24 items map[string]*list.Element 25 defaultExpiry time.Duration 26 name string 27 invalidateClusterEvent string 28 } 29 30 // LRUOptions contains options for initializing LRU cache 31 type LRUOptions struct { 32 Name string 33 Size int 34 DefaultExpiry time.Duration 35 InvalidateClusterEvent string 36 // StripedBuckets is used only by LRUStriped and shouldn't be greater than the number 37 // of CPUs available on the machine running this cache. 38 StripedBuckets int 39 } 40 41 // entry is used to hold a value in the evictList. 42 type entry struct { 43 key string 44 value []byte 45 expires time.Time 46 generation int64 47 } 48 49 // NewLRU creates an LRU of the given size. 50 func NewLRU(opts LRUOptions) Cache { 51 return &LRU{ 52 name: opts.Name, 53 size: opts.Size, 54 evictList: list.New(), 55 items: make(map[string]*list.Element, opts.Size), 56 defaultExpiry: opts.DefaultExpiry, 57 invalidateClusterEvent: opts.InvalidateClusterEvent, 58 } 59 } 60 61 // Purge is used to completely clear the cache. 62 func (l *LRU) Purge() error { 63 l.lock.Lock() 64 defer l.lock.Unlock() 65 66 l.len = 0 67 l.currentGeneration++ 68 return nil 69 } 70 71 // Set adds the given key and value to the store without an expiry. If the key already exists, 72 // it will overwrite the previous value. 73 func (l *LRU) Set(key string, value interface{}) error { 74 return l.SetWithExpiry(key, value, 0) 75 } 76 77 // SetWithDefaultExpiry adds the given key and value to the store with the default expiry. If 78 // the key already exists, it will overwrite the previoous value 79 func (l *LRU) SetWithDefaultExpiry(key string, value interface{}) error { 80 return l.SetWithExpiry(key, value, l.defaultExpiry) 81 } 82 83 // SetWithExpiry adds the given key and value to the cache with the given expiry. If the key 84 // already exists, it will overwrite the previoous value 85 func (l *LRU) SetWithExpiry(key string, value interface{}, ttl time.Duration) error { 86 return l.set(key, value, ttl) 87 } 88 89 // Get the content stored in the cache for the given key, and decode it into the value interface. 90 // return ErrKeyNotFound if the key is missing from the cache 91 func (l *LRU) Get(key string, value interface{}) error { 92 return l.get(key, value) 93 } 94 95 // Remove deletes the value for a key. 96 func (l *LRU) Remove(key string) error { 97 l.lock.Lock() 98 defer l.lock.Unlock() 99 100 if ent, ok := l.items[key]; ok { 101 l.removeElement(ent) 102 } 103 return nil 104 } 105 106 // Keys returns a slice of the keys in the cache. 107 func (l *LRU) Keys() ([]string, error) { 108 l.lock.RLock() 109 defer l.lock.RUnlock() 110 111 keys := make([]string, l.len) 112 i := 0 113 for ent := l.evictList.Back(); ent != nil; ent = ent.Prev() { 114 e := ent.Value.(*entry) 115 if e.generation == l.currentGeneration { 116 keys[i] = e.key 117 i++ 118 } 119 } 120 return keys, nil 121 } 122 123 // Len returns the number of items in the cache. 124 func (l *LRU) Len() (int, error) { 125 l.lock.RLock() 126 defer l.lock.RUnlock() 127 return l.len, nil 128 } 129 130 // GetInvalidateClusterEvent returns the cluster event configured when this cache was created. 131 func (l *LRU) GetInvalidateClusterEvent() string { 132 return l.invalidateClusterEvent 133 } 134 135 // Name returns the name of the cache 136 func (l *LRU) Name() string { 137 return l.name 138 } 139 140 func (l *LRU) set(key string, value interface{}, ttl time.Duration) error { 141 var expires time.Time 142 if ttl > 0 { 143 expires = time.Now().Add(ttl) 144 } 145 146 var buf []byte 147 var err error 148 // We use a fast path for hot structs. 149 if msgpVal, ok := value.(msgp.Marshaler); ok { 150 buf, err = msgpVal.MarshalMsg(nil) 151 } else { 152 // Slow path for other structs. 153 buf, err = msgpack.Marshal(value) 154 } 155 if err != nil { 156 return err 157 } 158 159 l.lock.Lock() 160 defer l.lock.Unlock() 161 162 // Check for existing item, ignoring expiry since we'd update anyway. 163 if ent, ok := l.items[key]; ok { 164 l.evictList.MoveToFront(ent) 165 e := ent.Value.(*entry) 166 e.value = buf 167 e.expires = expires 168 if e.generation != l.currentGeneration { 169 e.generation = l.currentGeneration 170 l.len++ 171 } 172 return nil 173 } 174 175 // Add new item 176 ent := &entry{key, buf, expires, l.currentGeneration} 177 entry := l.evictList.PushFront(ent) 178 l.items[key] = entry 179 l.len++ 180 181 if l.evictList.Len() > l.size { 182 l.removeElement(l.evictList.Back()) 183 } 184 return nil 185 } 186 187 func (l *LRU) get(key string, value interface{}) error { 188 val, err := l.getItem(key) 189 if err != nil { 190 return err 191 } 192 193 // We use a fast path for hot structs. 194 if msgpVal, ok := value.(msgp.Unmarshaler); ok { 195 _, err := msgpVal.UnmarshalMsg(val) 196 return err 197 } 198 199 // This is ugly and makes the cache package aware of the model package. 200 // But this is due to 2 things. 201 // 1. The msgp package works on methods on structs rather than functions. 202 // 2. Our cache interface passes pointers to empty pointers, and not pointers 203 // to values. This is mainly how all our model structs are passed around. 204 // It might be technically possible to use values _just_ for hot structs 205 // like these and then return a pointer while returning from the cache function, 206 // but it will make the codebase inconsistent, and has some edge-cases to take care of. 207 switch v := value.(type) { 208 case **model.User: 209 var u model.User 210 _, err := u.UnmarshalMsg(val) 211 *v = &u 212 return err 213 case *map[string]*model.User: 214 var u model.UserMap 215 _, err := u.UnmarshalMsg(val) 216 *v = u 217 return err 218 } 219 220 // Slow path for other structs. 221 return msgpack.Unmarshal(val, value) 222 } 223 224 func (l *LRU) getItem(key string) ([]byte, error) { 225 l.lock.Lock() 226 defer l.lock.Unlock() 227 228 ent, ok := l.items[key] 229 if !ok { 230 return nil, ErrKeyNotFound 231 } 232 e := ent.Value.(*entry) 233 if e.generation != l.currentGeneration || (!e.expires.IsZero() && time.Now().After(e.expires)) { 234 l.removeElement(ent) 235 return nil, ErrKeyNotFound 236 } 237 l.evictList.MoveToFront(ent) 238 return e.value, nil 239 } 240 241 func (l *LRU) removeElement(e *list.Element) { 242 l.evictList.Remove(e) 243 kv := e.Value.(*entry) 244 if kv.generation == l.currentGeneration { 245 l.len-- 246 } 247 delete(l.items, kv.key) 248 }