...
1
16
17 package cache
18
19 import (
20 "container/list"
21 "sync"
22 "time"
23 )
24
25
26 type Clock interface {
27 Now() time.Time
28 }
29
30
31 type realClock struct{}
32
33 func (realClock) Now() time.Time { return time.Now() }
34
35
36
37 type LRUExpireCache struct {
38
39 clock Clock
40
41 lock sync.Mutex
42
43 maxSize int
44 evictionList list.List
45 entries map[interface{}]*list.Element
46 }
47
48
49 func NewLRUExpireCache(maxSize int) *LRUExpireCache {
50 return NewLRUExpireCacheWithClock(maxSize, realClock{})
51 }
52
53
54 func NewLRUExpireCacheWithClock(maxSize int, clock Clock) *LRUExpireCache {
55 if maxSize <= 0 {
56 panic("maxSize must be > 0")
57 }
58
59 return &LRUExpireCache{
60 clock: clock,
61 maxSize: maxSize,
62 entries: map[interface{}]*list.Element{},
63 }
64 }
65
66 type cacheEntry struct {
67 key interface{}
68 value interface{}
69 expireTime time.Time
70 }
71
72
73 func (c *LRUExpireCache) Add(key interface{}, value interface{}, ttl time.Duration) {
74 c.lock.Lock()
75 defer c.lock.Unlock()
76
77
78 oldElement, ok := c.entries[key]
79 if ok {
80 c.evictionList.MoveToFront(oldElement)
81 oldElement.Value.(*cacheEntry).value = value
82 oldElement.Value.(*cacheEntry).expireTime = c.clock.Now().Add(ttl)
83 return
84 }
85
86
87 if c.evictionList.Len() >= c.maxSize {
88 toEvict := c.evictionList.Back()
89 c.evictionList.Remove(toEvict)
90 delete(c.entries, toEvict.Value.(*cacheEntry).key)
91 }
92
93
94 entry := &cacheEntry{
95 key: key,
96 value: value,
97 expireTime: c.clock.Now().Add(ttl),
98 }
99 element := c.evictionList.PushFront(entry)
100 c.entries[key] = element
101 }
102
103
104
105 func (c *LRUExpireCache) Get(key interface{}) (interface{}, bool) {
106 c.lock.Lock()
107 defer c.lock.Unlock()
108
109 element, ok := c.entries[key]
110 if !ok {
111 return nil, false
112 }
113
114 if c.clock.Now().After(element.Value.(*cacheEntry).expireTime) {
115 c.evictionList.Remove(element)
116 delete(c.entries, key)
117 return nil, false
118 }
119
120 c.evictionList.MoveToFront(element)
121
122 return element.Value.(*cacheEntry).value, true
123 }
124
125
126 func (c *LRUExpireCache) Remove(key interface{}) {
127 c.lock.Lock()
128 defer c.lock.Unlock()
129
130 element, ok := c.entries[key]
131 if !ok {
132 return
133 }
134
135 c.evictionList.Remove(element)
136 delete(c.entries, key)
137 }
138
139
140 func (c *LRUExpireCache) RemoveAll(predicate func(key any) bool) {
141 c.lock.Lock()
142 defer c.lock.Unlock()
143
144 for key, element := range c.entries {
145 if predicate(key) {
146 c.evictionList.Remove(element)
147 delete(c.entries, key)
148 }
149 }
150 }
151
152
153
154
155
156
157
158 func (c *LRUExpireCache) Keys() []interface{} {
159 c.lock.Lock()
160 defer c.lock.Unlock()
161
162 now := c.clock.Now()
163
164 val := make([]interface{}, 0, c.evictionList.Len())
165 for element := c.evictionList.Back(); element != nil; element = element.Prev() {
166
167 if !now.After(element.Value.(*cacheEntry).expireTime) {
168 val = append(val, element.Value.(*cacheEntry).key)
169 }
170 }
171
172 return val
173 }
174
View as plain text