lru.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package cache
  2. import (
  3. "context"
  4. "sort"
  5. "sync"
  6. "time"
  7. )
  8. // LRU represents an LRU (Least Recently Used) cache implementation.
  9. type LRU struct {
  10. // The maximum cache size in bytes. Default is 16MB.
  11. MaxSize int
  12. // The duration while an item is cached.
  13. ItemTTL time.Duration
  14. // The function called when an item is evicted.
  15. OnEvict func(key string, i Item)
  16. once sync.Once
  17. mutex sync.Mutex
  18. size int
  19. items map[string]*lruItem
  20. priority []*lruItem
  21. }
  22. func (c *LRU) Get(ctx context.Context, key string) (Item, bool) {
  23. c.once.Do(c.init)
  24. c.mutex.Lock()
  25. defer c.mutex.Unlock()
  26. i, isCached := c.items[key]
  27. if !isCached || i.expiresAt.Before(time.Now()) {
  28. return nil, false
  29. }
  30. i.count++
  31. return i.value, true
  32. }
  33. func (c *LRU) Set(ctx context.Context, key string, i Item) {
  34. c.once.Do(c.init)
  35. c.mutex.Lock()
  36. defer c.mutex.Unlock()
  37. if i, isCached := c.items[key]; isCached {
  38. i.expiresAt = time.Now()
  39. }
  40. if c.size+i.Size() > c.MaxSize {
  41. c.free(i.Size())
  42. }
  43. c.add(&lruItem{
  44. key: key,
  45. count: 1,
  46. expiresAt: time.Now().Add(c.ItemTTL),
  47. value: i,
  48. })
  49. }
  50. func (c *LRU) Del(ctx context.Context, key string) {
  51. c.once.Do(c.init)
  52. c.mutex.Lock()
  53. defer c.mutex.Unlock()
  54. if i, isCached := c.items[key]; isCached {
  55. i.expiresAt = time.Now()
  56. c.free(i.value.Size())
  57. }
  58. }
  59. func (c *LRU) Len() int {
  60. c.mutex.Lock()
  61. defer c.mutex.Unlock()
  62. return len(c.items)
  63. }
  64. func (c *LRU) Size() int {
  65. c.mutex.Lock()
  66. defer c.mutex.Unlock()
  67. return c.size
  68. }
  69. func (c *LRU) init() {
  70. if c.MaxSize <= 0 {
  71. c.MaxSize = 16000000
  72. }
  73. c.items = make(map[string]*lruItem, 64)
  74. c.priority = make([]*lruItem, 0, len(c.items))
  75. }
  76. func (c *LRU) free(size int) {
  77. now := time.Now()
  78. sortLRUItems(now, c.priority)
  79. for len(c.priority) != 0 {
  80. lastItem := c.priority[len(c.priority)-1]
  81. if lastItem.IsExpired(now) {
  82. c.removeLastItem()
  83. continue
  84. }
  85. if c.size+size <= c.MaxSize {
  86. return
  87. }
  88. c.removeLastItem()
  89. if c.OnEvict != nil {
  90. c.OnEvict(lastItem.key, lastItem.value)
  91. }
  92. }
  93. }
  94. func (c *LRU) removeLastItem() {
  95. i := len(c.priority) - 1
  96. item := c.priority[i]
  97. c.priority[i] = nil
  98. c.priority = c.priority[:i]
  99. delete(c.items, item.key)
  100. c.size -= item.value.Size()
  101. }
  102. func (c *LRU) add(i *lruItem) {
  103. c.items[i.key] = i
  104. c.priority = append(c.priority, i)
  105. c.size += i.value.Size()
  106. }
  107. type lruItem struct {
  108. key string
  109. count int
  110. expiresAt time.Time
  111. value Item
  112. }
  113. func (i *lruItem) priority(now time.Time) int {
  114. if i.IsExpired(now) {
  115. return 0
  116. }
  117. return i.count
  118. }
  119. func (i *lruItem) IsExpired(now time.Time) bool {
  120. return i.expiresAt.Before(now)
  121. }
  122. func sortLRUItems(now time.Time, v []*lruItem) {
  123. sort.Slice(v, func(a, b int) bool {
  124. return v[a].priority(now) > v[b].priority(now)
  125. })
  126. }