github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/commons/caches/lru/lru.go (about) 1 /* 2 * Copyright 2023 Wang Min Xiang 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package lru 19 20 import ( 21 "sync" 22 "time" 23 ) 24 25 type EvictCallback[K comparable, V any] func(key K, value V) 26 27 type LRU[K comparable, V any] struct { 28 size int 29 evictList *List[K, V] 30 items map[K]*Entry[K, V] 31 onEvict EvictCallback[K, V] 32 mu sync.Mutex 33 ttl time.Duration 34 done chan struct{} 35 buckets []bucket[K, V] 36 nextCleanupBucket uint8 37 } 38 39 type bucket[K comparable, V any] struct { 40 entries map[K]*Entry[K, V] 41 newestEntry time.Time 42 } 43 44 const numBuckets = 128 45 46 func NewWithExpire[K comparable, V any](size int, ttl time.Duration, onEvict EvictCallback[K, V]) *LRU[K, V] { 47 if size < 0 { 48 size = 0 49 } 50 res := LRU[K, V]{ 51 ttl: ttl, 52 size: size, 53 evictList: NewList[K, V](), 54 items: make(map[K]*Entry[K, V]), 55 onEvict: onEvict, 56 done: make(chan struct{}), 57 } 58 res.buckets = make([]bucket[K, V], numBuckets) 59 for i := 0; i < numBuckets; i++ { 60 res.buckets[i] = bucket[K, V]{entries: make(map[K]*Entry[K, V])} 61 } 62 63 if res.ttl > 0 { 64 go func(done <-chan struct{}) { 65 ticker := time.NewTicker(res.ttl / numBuckets) 66 defer ticker.Stop() 67 for { 68 select { 69 case <-done: 70 return 71 case <-ticker.C: 72 res.deleteExpired() 73 } 74 } 75 }(res.done) 76 } 77 return &res 78 } 79 80 func New[K comparable, V any](size int, onEvict EvictCallback[K, V]) *LRU[K, V] { 81 return NewWithExpire[K, V](size, 0, onEvict) 82 } 83 84 func (c *LRU[K, V]) Purge() { 85 c.mu.Lock() 86 defer c.mu.Unlock() 87 for k, v := range c.items { 88 if c.onEvict != nil { 89 c.onEvict(k, v.Value) 90 } 91 delete(c.items, k) 92 } 93 for _, b := range c.buckets { 94 for _, ent := range b.entries { 95 delete(b.entries, ent.Key) 96 } 97 } 98 c.evictList.Init() 99 } 100 101 func (c *LRU[K, V]) Add(key K, value V) (evicted bool) { 102 c.mu.Lock() 103 defer c.mu.Unlock() 104 105 if ent, ok := c.items[key]; ok { 106 c.evictList.MoveToFront(ent) 107 c.removeFromBucket(ent) 108 ent.Value = value 109 if c.ttl > 0 { 110 ent.ExpiresAt = time.Now().Add(c.ttl) 111 } 112 c.addToBucket(ent) 113 return false 114 } 115 expireAt := time.Time{} 116 if c.ttl > 0 { 117 expireAt = time.Now().Add(c.ttl) 118 } 119 120 ent := c.evictList.PushFrontExpirable(key, value, expireAt) 121 c.items[key] = ent 122 c.addToBucket(ent) 123 124 evict := c.size > 0 && c.evictList.Length() > c.size 125 if evict { 126 c.removeOldest() 127 } 128 return evict 129 } 130 131 func (c *LRU[K, V]) Get(key K) (value V, ok bool) { 132 c.mu.Lock() 133 defer c.mu.Unlock() 134 var ent *Entry[K, V] 135 if ent, ok = c.items[key]; ok { 136 if ent.ExpiresAt.IsZero() { 137 c.evictList.MoveToFront(ent) 138 return ent.Value, true 139 } 140 if time.Now().After(ent.ExpiresAt) { 141 return value, false 142 } 143 c.evictList.MoveToFront(ent) 144 return ent.Value, true 145 } 146 return 147 } 148 149 func (c *LRU[K, V]) Contains(key K) (ok bool) { 150 c.mu.Lock() 151 defer c.mu.Unlock() 152 _, ok = c.items[key] 153 return ok 154 } 155 156 func (c *LRU[K, V]) Peek(key K) (value V, ok bool) { 157 c.mu.Lock() 158 defer c.mu.Unlock() 159 var ent *Entry[K, V] 160 if ent, ok = c.items[key]; ok { 161 if ent.ExpiresAt.IsZero() { 162 c.evictList.MoveToFront(ent) 163 return ent.Value, true 164 } 165 if time.Now().After(ent.ExpiresAt) { 166 return value, false 167 } 168 return ent.Value, true 169 } 170 return 171 } 172 173 func (c *LRU[K, V]) Remove(key K) bool { 174 c.mu.Lock() 175 defer c.mu.Unlock() 176 if ent, ok := c.items[key]; ok { 177 c.removeElement(ent) 178 return true 179 } 180 return false 181 } 182 183 func (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) { 184 c.mu.Lock() 185 defer c.mu.Unlock() 186 if ent := c.evictList.Back(); ent != nil { 187 c.removeElement(ent) 188 return ent.Key, ent.Value, true 189 } 190 return 191 } 192 193 func (c *LRU[K, V]) GetOldest() (key K, value V, ok bool) { 194 c.mu.Lock() 195 defer c.mu.Unlock() 196 if ent := c.evictList.Back(); ent != nil { 197 return ent.Key, ent.Value, true 198 } 199 return 200 } 201 202 func (c *LRU[K, V]) Keys() []K { 203 c.mu.Lock() 204 defer c.mu.Unlock() 205 keys := make([]K, 0, len(c.items)) 206 now := time.Now() 207 for ent := c.evictList.Back(); ent != nil; ent = ent.PrevEntry() { 208 if !ent.ExpiresAt.IsZero() && now.After(ent.ExpiresAt) { 209 continue 210 } 211 keys = append(keys, ent.Key) 212 } 213 return keys 214 } 215 216 func (c *LRU[K, V]) Values() []V { 217 c.mu.Lock() 218 defer c.mu.Unlock() 219 values := make([]V, len(c.items)) 220 i := 0 221 now := time.Now() 222 for ent := c.evictList.Back(); ent != nil; ent = ent.PrevEntry() { 223 if !ent.ExpiresAt.IsZero() && now.After(ent.ExpiresAt) { 224 continue 225 } 226 values[i] = ent.Value 227 i++ 228 } 229 return values 230 } 231 232 func (c *LRU[K, V]) Len() int { 233 c.mu.Lock() 234 defer c.mu.Unlock() 235 return c.evictList.Length() 236 } 237 238 func (c *LRU[K, V]) Resize(size int) (evicted int) { 239 c.mu.Lock() 240 defer c.mu.Unlock() 241 if size <= 0 { 242 c.size = 0 243 return 0 244 } 245 diff := c.evictList.Length() - size 246 if diff < 0 { 247 diff = 0 248 } 249 for i := 0; i < diff; i++ { 250 c.removeOldest() 251 } 252 c.size = size 253 return diff 254 } 255 256 func (c *LRU[K, V]) removeOldest() { 257 if ent := c.evictList.Back(); ent != nil { 258 c.removeElement(ent) 259 } 260 } 261 262 func (c *LRU[K, V]) removeElement(e *Entry[K, V]) { 263 c.evictList.Remove(e) 264 delete(c.items, e.Key) 265 c.removeFromBucket(e) 266 if c.onEvict != nil { 267 c.onEvict(e.Key, e.Value) 268 } 269 } 270 271 func (c *LRU[K, V]) deleteExpired() { 272 if c.ttl < 1 { 273 return 274 } 275 c.mu.Lock() 276 bucketIdx := c.nextCleanupBucket 277 timeToExpire := time.Until(c.buckets[bucketIdx].newestEntry) 278 if timeToExpire > 0 { 279 c.mu.Unlock() 280 time.Sleep(timeToExpire) 281 c.mu.Lock() 282 } 283 for _, ent := range c.buckets[bucketIdx].entries { 284 c.removeElement(ent) 285 } 286 c.nextCleanupBucket = (c.nextCleanupBucket + 1) % numBuckets 287 c.mu.Unlock() 288 } 289 290 func (c *LRU[K, V]) addToBucket(e *Entry[K, V]) { 291 bucketId := (numBuckets + c.nextCleanupBucket - 1) % numBuckets 292 e.ExpireBucket = bucketId 293 c.buckets[bucketId].entries[e.Key] = e 294 if e.ExpiresAt.IsZero() || c.buckets[bucketId].newestEntry.Before(e.ExpiresAt) { 295 c.buckets[bucketId].newestEntry = e.ExpiresAt 296 } 297 } 298 299 func (c *LRU[K, V]) removeFromBucket(e *Entry[K, V]) { 300 delete(c.buckets[e.ExpireBucket].entries, e.Key) 301 }