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  }