...
1 package lru
2
3 import (
4 "fmt"
5 "sync"
6
7 "github.com/hashicorp/golang-lru/simplelru"
8 )
9
10 const (
11
12
13 Default2QRecentRatio = 0.25
14
15
16
17 Default2QGhostEntries = 0.50
18 )
19
20
21
22
23
24
25
26
27
28
29 type TwoQueueCache struct {
30 size int
31 recentSize int
32
33 recent simplelru.LRUCache
34 frequent simplelru.LRUCache
35 recentEvict simplelru.LRUCache
36 lock sync.RWMutex
37 }
38
39
40
41 func New2Q(size int) (*TwoQueueCache, error) {
42 return New2QParams(size, Default2QRecentRatio, Default2QGhostEntries)
43 }
44
45
46
47 func New2QParams(size int, recentRatio, ghostRatio float64) (*TwoQueueCache, error) {
48 if size <= 0 {
49 return nil, fmt.Errorf("invalid size")
50 }
51 if recentRatio < 0.0 || recentRatio > 1.0 {
52 return nil, fmt.Errorf("invalid recent ratio")
53 }
54 if ghostRatio < 0.0 || ghostRatio > 1.0 {
55 return nil, fmt.Errorf("invalid ghost ratio")
56 }
57
58
59 recentSize := int(float64(size) * recentRatio)
60 evictSize := int(float64(size) * ghostRatio)
61
62
63 recent, err := simplelru.NewLRU(size, nil)
64 if err != nil {
65 return nil, err
66 }
67 frequent, err := simplelru.NewLRU(size, nil)
68 if err != nil {
69 return nil, err
70 }
71 recentEvict, err := simplelru.NewLRU(evictSize, nil)
72 if err != nil {
73 return nil, err
74 }
75
76
77 c := &TwoQueueCache{
78 size: size,
79 recentSize: recentSize,
80 recent: recent,
81 frequent: frequent,
82 recentEvict: recentEvict,
83 }
84 return c, nil
85 }
86
87
88 func (c *TwoQueueCache) Get(key interface{}) (value interface{}, ok bool) {
89 c.lock.Lock()
90 defer c.lock.Unlock()
91
92
93 if val, ok := c.frequent.Get(key); ok {
94 return val, ok
95 }
96
97
98
99 if val, ok := c.recent.Peek(key); ok {
100 c.recent.Remove(key)
101 c.frequent.Add(key, val)
102 return val, ok
103 }
104
105
106 return nil, false
107 }
108
109
110 func (c *TwoQueueCache) Add(key, value interface{}) {
111 c.lock.Lock()
112 defer c.lock.Unlock()
113
114
115
116 if c.frequent.Contains(key) {
117 c.frequent.Add(key, value)
118 return
119 }
120
121
122
123 if c.recent.Contains(key) {
124 c.recent.Remove(key)
125 c.frequent.Add(key, value)
126 return
127 }
128
129
130
131 if c.recentEvict.Contains(key) {
132 c.ensureSpace(true)
133 c.recentEvict.Remove(key)
134 c.frequent.Add(key, value)
135 return
136 }
137
138
139 c.ensureSpace(false)
140 c.recent.Add(key, value)
141 }
142
143
144 func (c *TwoQueueCache) ensureSpace(recentEvict bool) {
145
146 recentLen := c.recent.Len()
147 freqLen := c.frequent.Len()
148 if recentLen+freqLen < c.size {
149 return
150 }
151
152
153
154 if recentLen > 0 && (recentLen > c.recentSize || (recentLen == c.recentSize && !recentEvict)) {
155 k, _, _ := c.recent.RemoveOldest()
156 c.recentEvict.Add(k, nil)
157 return
158 }
159
160
161 c.frequent.RemoveOldest()
162 }
163
164
165 func (c *TwoQueueCache) Len() int {
166 c.lock.RLock()
167 defer c.lock.RUnlock()
168 return c.recent.Len() + c.frequent.Len()
169 }
170
171
172
173 func (c *TwoQueueCache) Keys() []interface{} {
174 c.lock.RLock()
175 defer c.lock.RUnlock()
176 k1 := c.frequent.Keys()
177 k2 := c.recent.Keys()
178 return append(k1, k2...)
179 }
180
181
182 func (c *TwoQueueCache) Remove(key interface{}) {
183 c.lock.Lock()
184 defer c.lock.Unlock()
185 if c.frequent.Remove(key) {
186 return
187 }
188 if c.recent.Remove(key) {
189 return
190 }
191 if c.recentEvict.Remove(key) {
192 return
193 }
194 }
195
196
197 func (c *TwoQueueCache) Purge() {
198 c.lock.Lock()
199 defer c.lock.Unlock()
200 c.recent.Purge()
201 c.frequent.Purge()
202 c.recentEvict.Purge()
203 }
204
205
206
207 func (c *TwoQueueCache) Contains(key interface{}) bool {
208 c.lock.RLock()
209 defer c.lock.RUnlock()
210 return c.frequent.Contains(key) || c.recent.Contains(key)
211 }
212
213
214
215 func (c *TwoQueueCache) Peek(key interface{}) (value interface{}, ok bool) {
216 c.lock.RLock()
217 defer c.lock.RUnlock()
218 if val, ok := c.frequent.Peek(key); ok {
219 return val, ok
220 }
221 return c.recent.Peek(key)
222 }
223
View as plain text