-
Notifications
You must be signed in to change notification settings - Fork 9
/
cache.go
273 lines (240 loc) · 6.25 KB
/
cache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
// Package libcache provides in-memory caches based on different caches replacement algorithms.
package libcache
import (
"context"
"sync"
"time"
"github.com/shaj13/libcache/internal"
)
// These are the generalized cache operations that can trigger a event.
const (
Read = internal.Read
Write = internal.Write
Remove = internal.Remove
)
// Op describes a set of cache operations.
type Op = internal.Op
// Event represents a single cache entry change.
type Event = internal.Event
// Cache stores data so that future requests for that data can be served faster.
type Cache interface {
// Load returns key value.
Load(key interface{}) (interface{}, bool)
// Peek returns key value without updating the underlying "recent-ness".
Peek(key interface{}) (interface{}, bool)
// Update the key value without updating the underlying "recent-ness".
Update(key interface{}, value interface{})
// Store sets the key value.
Store(key interface{}, value interface{})
// StoreWithTTL sets the key value with TTL overrides the default.
StoreWithTTL(key interface{}, value interface{}, ttl time.Duration)
// Delete deletes the key value.
Delete(key interface{})
// Expiry returns key value expiry time.
Expiry(key interface{}) (time.Time, bool)
// Keys return cache records keys.
Keys() []interface{}
// Contains Checks if a key exists in cache.
Contains(key interface{}) bool
// Purge Clears all cache entries.
Purge()
// Resize cache, returning number evicted
Resize(int) int
// Len Returns the number of items in the cache.
Len() int
// Cap Returns the cache capacity.
Cap() int
// TTL returns entries default TTL.
TTL() time.Duration
// SetTTL sets entries default TTL.
SetTTL(time.Duration)
// RegisterOnEvicted registers a function,
// to call it when an entry is purged from the cache.
//
// Deprecated: use Notify instead.
RegisterOnEvicted(f func(key, value interface{}))
// RegisterOnExpired registers a function,
// to call it when an entry TTL elapsed.
//
// Deprecated: use Notify instead.
RegisterOnExpired(f func(key, value interface{}))
// Notify causes cache to relay events to ch.
// If no operations are provided, all incoming operations will be relayed to ch.
// Otherwise, just the provided operations will.
Notify(ch chan<- Event, ops ...Op)
// Ignore causes the provided operations to be ignored. Ignore undoes the effect
// of any prior calls to Notify for the provided operations.
// If no operations are provided, ch removed.
Ignore(ch chan<- Event, ops ...Op)
// GC runs a garbage collection and blocks the caller until the
// all expired items from the cache evicted.
//
// GC returns the remaining time duration for the next gc cycle
// if there any, Otherwise, it return 0.
//
// Calling GC without waits for the duration to elapsed considered a no-op.
GC() time.Duration
}
// GC runs a garbage collection to evict expired items from the cache on time.
//
// GC trace expired items based on read-write barrier, therefore it listen to
// cache write events and capture the result of calling the GC method on cache
// to trigger the garbage collection loop at the right point in time.
//
// GC is a long running function, it returns when ctx done, therefore the
// caller must start it in its own goroutine.
//
// Experimental
//
// Notice: This func is EXPERIMENTAL and may be changed or removed in a
// later release.
func GC(ctx context.Context, cache Cache) {
remaining := time.Duration(0)
t := time.NewTimer(remaining)
defer t.Stop()
c := make(chan Event, 1)
cache.Notify(c, Write)
defer func() {
cache.Ignore(c)
close(c)
}()
gc := func() {
remaining = cache.GC()
t.Stop()
if remaining > 0 {
t.Reset(remaining)
}
}
for {
select {
case e := <-c:
if e.Expiry.IsZero() {
continue
}
if remaining == 0 || time.Until(e.Expiry) < remaining {
gc()
}
case <-t.C:
gc()
case <-ctx.Done():
return
}
}
}
type cache struct {
// mu guards unsafe cache.
// Calls to mu.Unlock are currently not deferred,
// because defer adds ~200 ns (as of go1.)
mu sync.Mutex
unsafe Cache
}
func (c *cache) Load(key interface{}) (interface{}, bool) {
c.mu.Lock()
v, ok := c.unsafe.Load(key)
c.mu.Unlock()
return v, ok
}
func (c *cache) Peek(key interface{}) (interface{}, bool) {
c.mu.Lock()
v, ok := c.unsafe.Peek(key)
c.mu.Unlock()
return v, ok
}
func (c *cache) Update(key interface{}, value interface{}) {
c.mu.Lock()
c.unsafe.Update(key, value)
c.mu.Unlock()
}
func (c *cache) Store(key interface{}, value interface{}) {
c.mu.Lock()
c.unsafe.Store(key, value)
c.mu.Unlock()
}
func (c *cache) StoreWithTTL(key interface{}, value interface{}, ttl time.Duration) {
c.mu.Lock()
c.unsafe.StoreWithTTL(key, value, ttl)
c.mu.Unlock()
}
func (c *cache) Delete(key interface{}) {
c.mu.Lock()
c.unsafe.Delete(key)
c.mu.Unlock()
}
func (c *cache) Keys() []interface{} {
c.mu.Lock()
keys := c.unsafe.Keys()
c.mu.Unlock()
return keys
}
func (c *cache) Contains(key interface{}) bool {
c.mu.Lock()
ok := c.unsafe.Contains(key)
c.mu.Unlock()
return ok
}
func (c *cache) Purge() {
c.mu.Lock()
c.unsafe.Purge()
c.mu.Unlock()
}
func (c *cache) Resize(s int) int {
c.mu.Lock()
n := c.unsafe.Resize(s)
c.mu.Unlock()
return n
}
func (c *cache) Len() int {
c.mu.Lock()
n := c.unsafe.Len()
c.mu.Unlock()
return n
}
func (c *cache) Cap() int {
c.mu.Lock()
n := c.unsafe.Cap()
c.mu.Unlock()
return n
}
func (c *cache) TTL() time.Duration {
c.mu.Lock()
ttl := c.unsafe.TTL()
c.mu.Unlock()
return ttl
}
func (c *cache) SetTTL(ttl time.Duration) {
c.mu.Lock()
c.unsafe.SetTTL(ttl)
c.mu.Unlock()
}
func (c *cache) RegisterOnEvicted(f func(key, value interface{})) {
c.mu.Lock()
c.unsafe.RegisterOnEvicted(f)
c.mu.Unlock()
}
func (c *cache) RegisterOnExpired(f func(key, value interface{})) {
c.mu.Lock()
c.unsafe.RegisterOnExpired(f)
c.mu.Unlock()
}
func (c *cache) Notify(ch chan<- Event, ops ...Op) {
c.mu.Lock()
c.unsafe.Notify(ch, ops...)
c.mu.Unlock()
}
func (c *cache) Ignore(ch chan<- Event, ops ...Op) {
c.mu.Lock()
c.unsafe.Ignore(ch, ops...)
c.mu.Unlock()
}
func (c *cache) Expiry(key interface{}) (time.Time, bool) {
c.mu.Lock()
exp, ok := c.unsafe.Expiry(key)
c.mu.Unlock()
return exp, ok
}
func (c *cache) GC() time.Duration {
c.mu.Lock()
dur := c.unsafe.GC()
c.mu.Unlock()
return dur
}