github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/soliton/kvcache/simple_lru.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package ekvcache 15 16 import ( 17 "container/list" 18 19 "github.com/whtcorpsinc/errors" 20 "github.com/whtcorpsinc/milevadb/soliton/memory" 21 ) 22 23 // Key is the interface that every key in LRU Cache should implement. 24 type Key interface { 25 Hash() []byte 26 } 27 28 // Value is the interface that every value in LRU Cache should implement. 29 type Value interface { 30 } 31 32 // cacheEntry wraps Key and Value. It's the value of list.Element. 33 type cacheEntry struct { 34 key Key 35 value Value 36 } 37 38 var ( 39 // GlobalLRUMemUsageTracker tracks all the memory usage of SimpleLRUCache 40 GlobalLRUMemUsageTracker *memory.Tracker 41 ) 42 43 const ( 44 // ProfileName is the function name in heap profile 45 ProfileName = "github.com/whtcorpsinc/milevadb/soliton/ekvcache.(*SimpleLRUCache).Put" 46 ) 47 48 func init() { 49 GlobalLRUMemUsageTracker = memory.NewTracker(memory.LabelForGlobalSimpleLRUCache, -1) 50 } 51 52 // SimpleLRUCache is a simple least recently used cache, not thread-safe, use it carefully. 53 type SimpleLRUCache struct { 54 capacity uint 55 size uint 56 // 0 indicates no quota 57 quota uint64 58 guard float64 59 elements map[string]*list.Element 60 cache *list.List 61 } 62 63 // NewSimpleLRUCache creates a SimpleLRUCache object, whose capacity is "capacity". 64 // NOTE: "capacity" should be a positive value. 65 func NewSimpleLRUCache(capacity uint, guard float64, quota uint64) *SimpleLRUCache { 66 if capacity < 1 { 67 panic("capacity of LRU Cache should be at least 1.") 68 } 69 return &SimpleLRUCache{ 70 capacity: capacity, 71 size: 0, 72 quota: quota, 73 guard: guard, 74 elements: make(map[string]*list.Element), 75 cache: list.New(), 76 } 77 } 78 79 // Get tries to find the corresponding value according to the given key. 80 func (l *SimpleLRUCache) Get(key Key) (value Value, ok bool) { 81 element, exists := l.elements[string(key.Hash())] 82 if !exists { 83 return nil, false 84 } 85 l.cache.MoveToFront(element) 86 return element.Value.(*cacheEntry).value, true 87 } 88 89 // Put puts the (key, value) pair into the LRU Cache. 90 func (l *SimpleLRUCache) Put(key Key, value Value) { 91 hash := string(key.Hash()) 92 element, exists := l.elements[hash] 93 if exists { 94 l.cache.MoveToFront(element) 95 return 96 } 97 98 newCacheEntry := &cacheEntry{ 99 key: key, 100 value: value, 101 } 102 103 element = l.cache.PushFront(newCacheEntry) 104 l.elements[hash] = element 105 l.size++ 106 // Getting used memory is expensive and can be avoided by setting quota to 0. 107 if l.quota == 0 { 108 if l.size > l.capacity { 109 lru := l.cache.Back() 110 l.cache.Remove(lru) 111 delete(l.elements, string(lru.Value.(*cacheEntry).key.Hash())) 112 l.size-- 113 } 114 return 115 } 116 117 memUsed, err := memory.MemUsed() 118 if err != nil { 119 l.DeleteAll() 120 return 121 } 122 123 for memUsed > uint64(float64(l.quota)*(1.0-l.guard)) || l.size > l.capacity { 124 lru := l.cache.Back() 125 if lru == nil { 126 break 127 } 128 l.cache.Remove(lru) 129 delete(l.elements, string(lru.Value.(*cacheEntry).key.Hash())) 130 l.size-- 131 if memUsed > uint64(float64(l.quota)*(1.0-l.guard)) { 132 memUsed, err = memory.MemUsed() 133 if err != nil { 134 l.DeleteAll() 135 return 136 } 137 } 138 } 139 } 140 141 // Delete deletes the key-value pair from the LRU Cache. 142 func (l *SimpleLRUCache) Delete(key Key) { 143 k := string(key.Hash()) 144 element := l.elements[k] 145 if element == nil { 146 return 147 } 148 l.cache.Remove(element) 149 delete(l.elements, k) 150 l.size-- 151 } 152 153 // DeleteAll deletes all elements from the LRU Cache. 154 func (l *SimpleLRUCache) DeleteAll() { 155 for lru := l.cache.Back(); lru != nil; lru = l.cache.Back() { 156 l.cache.Remove(lru) 157 delete(l.elements, string(lru.Value.(*cacheEntry).key.Hash())) 158 l.size-- 159 } 160 } 161 162 // Size gets the current cache size. 163 func (l *SimpleLRUCache) Size() int { 164 return int(l.size) 165 } 166 167 // Values return all values in cache. 168 func (l *SimpleLRUCache) Values() []Value { 169 values := make([]Value, 0, l.cache.Len()) 170 for ele := l.cache.Front(); ele != nil; ele = ele.Next() { 171 value := ele.Value.(*cacheEntry).value 172 values = append(values, value) 173 } 174 return values 175 } 176 177 // Keys return all keys in cache. 178 func (l *SimpleLRUCache) Keys() []Key { 179 keys := make([]Key, 0, l.cache.Len()) 180 for ele := l.cache.Front(); ele != nil; ele = ele.Next() { 181 key := ele.Value.(*cacheEntry).key 182 keys = append(keys, key) 183 } 184 return keys 185 } 186 187 // SetCapacity sets capacity of the cache. 188 func (l *SimpleLRUCache) SetCapacity(capacity uint) error { 189 if capacity < 1 { 190 return errors.New("capacity of lru cache should be at least 1") 191 } 192 l.capacity = capacity 193 for l.size > l.capacity { 194 lru := l.cache.Back() 195 l.cache.Remove(lru) 196 delete(l.elements, string(lru.Value.(*cacheEntry).key.Hash())) 197 l.size-- 198 } 199 return nil 200 } 201 202 // RemoveOldest removes the oldest element from the cache. 203 func (l *SimpleLRUCache) RemoveOldest() (key Key, value Value, ok bool) { 204 if l.size > 0 { 205 ele := l.cache.Back() 206 l.cache.Remove(ele) 207 delete(l.elements, string(ele.Value.(*cacheEntry).key.Hash())) 208 l.size-- 209 return ele.Value.(*cacheEntry).key, ele.Value.(*cacheEntry).value, true 210 } 211 return nil, nil, false 212 }