lrubank.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package stringutil
  2. import (
  3. "container/heap"
  4. "sync"
  5. "time"
  6. )
  7. type lruEntry struct {
  8. value string
  9. used time.Time
  10. }
  11. type heapEntry struct {
  12. *lruEntry
  13. key string
  14. }
  15. type maxHeap []*heapEntry
  16. func (h maxHeap) Len() int { return len(h) }
  17. func (h maxHeap) Less(i, j int) bool { return h[i].used.After(h[j].used) } // newer = "larger"
  18. func (h maxHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
  19. func (h *maxHeap) Push(x any) {
  20. *h = append(*h, x.(*heapEntry))
  21. }
  22. func (h *maxHeap) Pop() any {
  23. old := *h
  24. n := len(old)
  25. x := old[n-1]
  26. *h = old[:n-1]
  27. return x
  28. }
  29. func nOldest(arr []*heapEntry, n int) []*heapEntry {
  30. if n <= 0 {
  31. return []*heapEntry{}
  32. }
  33. if n >= len(arr) {
  34. return arr
  35. }
  36. h := maxHeap(arr[:n])
  37. heap.Init(&h)
  38. for _, entry := range arr[n:] {
  39. // swap in oldest, re-heapify
  40. if entry.used.Before(h[0].used) {
  41. h[0] = entry
  42. heap.Fix(&h, 0)
  43. }
  44. }
  45. return []*heapEntry(h)
  46. }
  47. type lruStringBank struct {
  48. lock sync.Mutex
  49. stop chan struct{}
  50. m map[string]*lruEntry
  51. }
  52. func NewLruStringBank(capacity int, evictionInterval time.Duration) StringBank {
  53. stop := make(chan struct{})
  54. bank := &lruStringBank{
  55. m: make(map[string]*lruEntry),
  56. }
  57. go func() {
  58. for {
  59. select {
  60. case <-stop:
  61. return
  62. case <-time.After(evictionInterval):
  63. }
  64. // need to take the lock during eviction
  65. bank.lock.Lock()
  66. if len(bank.m) <= capacity {
  67. bank.lock.Unlock()
  68. continue
  69. }
  70. // we collect a list of all lru entries so we can max heap the first n elements
  71. arr := make([]*heapEntry, 0, len(bank.m))
  72. for k, v := range bank.m {
  73. arr = append(arr, &heapEntry{key: k, lruEntry: v})
  74. }
  75. oldest := nOldest(arr, len(bank.m)-capacity)
  76. for _, old := range oldest {
  77. delete(bank.m, old.key)
  78. }
  79. bank.lock.Unlock()
  80. }
  81. }()
  82. return bank
  83. }
  84. func (sb *lruStringBank) Stop() {
  85. sb.lock.Lock()
  86. defer sb.lock.Unlock()
  87. if sb.stop != nil {
  88. close(sb.stop)
  89. sb.stop = nil
  90. }
  91. }
  92. func (sb *lruStringBank) LoadOrStore(key, value string) (string, bool) {
  93. sb.lock.Lock()
  94. if v, ok := sb.m[key]; ok {
  95. v.used = time.Now().UTC()
  96. sb.lock.Unlock()
  97. return v.value, ok
  98. }
  99. sb.m[key] = &lruEntry{
  100. value: value,
  101. used: time.Now().UTC(),
  102. }
  103. sb.lock.Unlock()
  104. return value, false
  105. }
  106. func (sb *lruStringBank) LoadOrStoreFunc(key string, f func() string) (string, bool) {
  107. sb.lock.Lock()
  108. if v, ok := sb.m[key]; ok {
  109. v.used = time.Now().UTC()
  110. sb.lock.Unlock()
  111. return v.value, ok
  112. }
  113. // create the key and value using the func (the key could be deallocated later)
  114. value := f()
  115. sb.m[key] = &lruEntry{
  116. value: value,
  117. used: time.Now().UTC(),
  118. }
  119. sb.lock.Unlock()
  120. return value, false
  121. }
  122. func (sb *lruStringBank) Clear() {
  123. sb.lock.Lock()
  124. sb.m = make(map[string]*lruEntry)
  125. sb.lock.Unlock()
  126. }