lru.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. // Copyright 2019 Yunion
  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. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Copyright 2012, Google Inc. All rights reserved.
  15. // Use of this source code is governed by a BSD-style
  16. // license that can be found in the LICENSE file.
  17. // Package cache implements a LRU cache.
  18. //
  19. // The implementation borrows heavily from SmallLRUCache
  20. // (originally by Nathan Schrenk). The object maintains a doubly-linked list of
  21. // elements. When an element is accessed, it is promoted to the head of the
  22. // list. When space is needed, the element at the tail of the list
  23. // (the least recently used element) is evicted.
  24. package cache
  25. import (
  26. "container/list"
  27. "fmt"
  28. "sync"
  29. "time"
  30. )
  31. // LRUCache is a typical LRU cache implementation. If the cache
  32. // reaches the capacity, the least recently used item is deleted from
  33. // the cache. Note the capacity is not the number of items, but the
  34. // total sum of the Size() of each item.
  35. type LRUCache struct {
  36. mu sync.Mutex
  37. // list & table of *entry objects
  38. list *list.List
  39. table map[string]*list.Element
  40. // Our current size. Obviously a gross simplification and
  41. // low-grade approximation.
  42. size int64
  43. // How much we are limiting the cache to.
  44. capacity int64
  45. }
  46. // Value is the interface values that go into LRUCache need to satisfy
  47. type Value interface {
  48. // Size returns how big this value is.
  49. Size() int
  50. }
  51. // Item is what is stored in the cache
  52. type Item struct {
  53. Key string
  54. Value Value
  55. }
  56. type entry struct {
  57. key string
  58. value Value
  59. size int64
  60. time_accessed time.Time
  61. }
  62. // NewLRUCache creates a new empty cache with the given capacity.
  63. func NewLRUCache(capacity int64) *LRUCache {
  64. return &LRUCache{
  65. list: list.New(),
  66. table: make(map[string]*list.Element),
  67. capacity: capacity,
  68. }
  69. }
  70. // Get returns a value from the cache, and marks the entry as most
  71. // recently used.
  72. func (lru *LRUCache) Get(key string) (v Value, ok bool) {
  73. lru.mu.Lock()
  74. defer lru.mu.Unlock()
  75. element := lru.table[key]
  76. if element == nil {
  77. return nil, false
  78. }
  79. lru.moveToFront(element)
  80. return element.Value.(*entry).value, true
  81. }
  82. // Set sets a value in the cache.
  83. func (lru *LRUCache) Set(key string, value Value) {
  84. lru.mu.Lock()
  85. defer lru.mu.Unlock()
  86. if element := lru.table[key]; element != nil {
  87. lru.updateInplace(element, value)
  88. } else {
  89. lru.addNew(key, value)
  90. }
  91. }
  92. // SetIfAbsent will set the value in the cache if not present. If the
  93. // value exists in the cache, we don't set it.
  94. func (lru *LRUCache) SetIfAbsent(key string, value Value) {
  95. lru.mu.Lock()
  96. defer lru.mu.Unlock()
  97. if element := lru.table[key]; element != nil {
  98. lru.moveToFront(element)
  99. } else {
  100. lru.addNew(key, value)
  101. }
  102. }
  103. // Delete removes an entry from the cache, and returns if the entry existed.
  104. func (lru *LRUCache) Delete(key string) bool {
  105. lru.mu.Lock()
  106. defer lru.mu.Unlock()
  107. element := lru.table[key]
  108. if element == nil {
  109. return false
  110. }
  111. lru.list.Remove(element)
  112. delete(lru.table, key)
  113. lru.size -= element.Value.(*entry).size
  114. return true
  115. }
  116. // Clear will clear the entire cache.
  117. func (lru *LRUCache) Clear() {
  118. lru.mu.Lock()
  119. defer lru.mu.Unlock()
  120. lru.list.Init()
  121. lru.table = make(map[string]*list.Element)
  122. lru.size = 0
  123. }
  124. // SetCapacity will set the capacity of the cache. If the capacity is
  125. // smaller, and the current cache size exceed that capacity, the cache
  126. // will be shrank.
  127. func (lru *LRUCache) SetCapacity(capacity int64) {
  128. lru.mu.Lock()
  129. defer lru.mu.Unlock()
  130. lru.capacity = capacity
  131. lru.checkCapacity()
  132. }
  133. // Stats returns a few stats on the cache.
  134. func (lru *LRUCache) Stats() (length, size, capacity int64, oldest time.Time) {
  135. lru.mu.Lock()
  136. defer lru.mu.Unlock()
  137. if lastElem := lru.list.Back(); lastElem != nil {
  138. oldest = lastElem.Value.(*entry).time_accessed
  139. }
  140. return int64(lru.list.Len()), lru.size, lru.capacity, oldest
  141. }
  142. // StatsJSON returns stats as a JSON object in a string.
  143. func (lru *LRUCache) StatsJSON() string {
  144. if lru == nil {
  145. return "{}"
  146. }
  147. l, s, c, o := lru.Stats()
  148. return fmt.Sprintf("{\"Length\": %v, \"Size\": %v, \"Capacity\": %v, \"OldestAccess\": \"%v\"}", l, s, c, o)
  149. }
  150. // Length returns how many elements are in the cache
  151. func (lru *LRUCache) Length() int64 {
  152. lru.mu.Lock()
  153. defer lru.mu.Unlock()
  154. return int64(lru.list.Len())
  155. }
  156. // Size returns the sum of the objects' Size() method.
  157. func (lru *LRUCache) Size() int64 {
  158. lru.mu.Lock()
  159. defer lru.mu.Unlock()
  160. return lru.size
  161. }
  162. // Capacity returns the cache maximum capacity.
  163. func (lru *LRUCache) Capacity() int64 {
  164. lru.mu.Lock()
  165. defer lru.mu.Unlock()
  166. return lru.capacity
  167. }
  168. // Oldest returns the insertion time of the oldest element in the cache,
  169. // or a IsZero() time if cache is empty.
  170. func (lru *LRUCache) Oldest() (oldest time.Time) {
  171. lru.mu.Lock()
  172. defer lru.mu.Unlock()
  173. if lastElem := lru.list.Back(); lastElem != nil {
  174. oldest = lastElem.Value.(*entry).time_accessed
  175. }
  176. return
  177. }
  178. // Keys returns all the keys for the cache, ordered from most recently
  179. // used to last recently used.
  180. func (lru *LRUCache) Keys() []string {
  181. lru.mu.Lock()
  182. defer lru.mu.Unlock()
  183. keys := make([]string, 0, lru.list.Len())
  184. for e := lru.list.Front(); e != nil; e = e.Next() {
  185. keys = append(keys, e.Value.(*entry).key)
  186. }
  187. return keys
  188. }
  189. // Items returns all the values for the cache, ordered from most recently
  190. // used to last recently used.
  191. func (lru *LRUCache) Items() []Item {
  192. lru.mu.Lock()
  193. defer lru.mu.Unlock()
  194. items := make([]Item, 0, lru.list.Len())
  195. for e := lru.list.Front(); e != nil; e = e.Next() {
  196. v := e.Value.(*entry)
  197. items = append(items, Item{Key: v.key, Value: v.value})
  198. }
  199. return items
  200. }
  201. func (lru *LRUCache) updateInplace(element *list.Element, value Value) {
  202. valueSize := int64(value.Size())
  203. sizeDiff := valueSize - element.Value.(*entry).size
  204. element.Value.(*entry).value = value
  205. element.Value.(*entry).size = valueSize
  206. lru.size += sizeDiff
  207. lru.moveToFront(element)
  208. lru.checkCapacity()
  209. }
  210. func (lru *LRUCache) moveToFront(element *list.Element) {
  211. lru.list.MoveToFront(element)
  212. element.Value.(*entry).time_accessed = time.Now()
  213. }
  214. func (lru *LRUCache) addNew(key string, value Value) {
  215. newEntry := &entry{key, value, int64(value.Size()), time.Now()}
  216. element := lru.list.PushFront(newEntry)
  217. lru.table[key] = element
  218. lru.size += newEntry.size
  219. lru.checkCapacity()
  220. }
  221. func (lru *LRUCache) checkCapacity() {
  222. // Partially duplicated from Delete
  223. for lru.size > lru.capacity {
  224. delElem := lru.list.Back()
  225. delValue := delElem.Value.(*entry)
  226. lru.list.Remove(delElem)
  227. delete(lru.table, delValue.key)
  228. lru.size -= delValue.size
  229. }
  230. }