Skip to content

Commit 5754ec5

Browse files
committed
Add OnEvict to options
1 parent e2a55ce commit 5754ec5

File tree

2 files changed

+47
-24
lines changed

2 files changed

+47
-24
lines changed

‎lazycache.go‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,18 @@ import (
77
)
88

99
// New creates a new Cache.
10-
func New[K comparable, V any](options Options) *Cache[K, V] {
11-
lru, err := simplelru.NewLRU[K, *valueWrapper[V]](int(options.MaxEntries), nil)
10+
func New[K comparable, V any](options Options[K, V]) *Cache[K, V] {
11+
var onEvict simplelru.EvictCallback[K, *valueWrapper[V]] = nil
12+
if options.OnEvict != nil {
13+
onEvict = func(key K, value *valueWrapper[V]) {
14+
value.wait()
15+
if value.found {
16+
options.OnEvict(key, value.value)
17+
}
18+
}
19+
}
20+
21+
lru, err := simplelru.NewLRU[K, *valueWrapper[V]](int(options.MaxEntries), onEvict)
1222
if err != nil {
1323
panic(err)
1424
}
@@ -19,10 +29,13 @@ func New[K comparable, V any](options Options) *Cache[K, V] {
1929
}
2030

2131
// Options holds the cache options.
22-
type Options struct {
32+
type Options[K comparable, V any] struct {
2333
// MaxEntries is the maximum number of entries that the cache should hold.
2434
// Note that this can also be adjusted after the cache is created with Resize.
2535
MaxEntries int
36+
37+
// OnEvict is an optional callback that is called when an entry is evicted.
38+
OnEvict func(key K, value V)
2639
}
2740

2841
// Cache is a thread-safe resizable LRU cache.

‎lazycache_test.go‎

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
func TestCache(t *testing.T) {
1616
c := qt.New(t)
1717

18-
cache := New[int, any](Options{MaxEntries: 10})
18+
cache := New[int, any](Options[int, any]{MaxEntries: 10})
1919

2020
get := func(key int) any {
2121
v, found := cache.Get(key)
@@ -54,15 +54,14 @@ func TestCache(t *testing.T) {
5454

5555
c.Assert(cache.len(), qt.Equals, 0)
5656

57-
c.Assert(func() { New[int, any](Options{MaxEntries: -1}) }, qt.PanicMatches, "must provide a positive size")
58-
57+
c.Assert(func() { New[int, any](Options[int, any]{MaxEntries: -1}) }, qt.PanicMatches, "must provide a positive size")
5958
}
6059

6160
func TestDeleteFunc(t *testing.T) {
6261
c := qt.New(t)
6362

6463
c.Run("Basic", func(c *qt.C) {
65-
cache := New[int, any](Options{MaxEntries: 1000})
64+
cache := New(Options[int, any]{MaxEntries: 1000})
6665
for i := 0; i < 10; i++ {
6766
cache.Set(i, i)
6867
}
@@ -73,13 +72,12 @@ func TestDeleteFunc(t *testing.T) {
7372
})
7473

7574
c.Run("Temporary", func(c *qt.C) {
76-
7775
var wg sync.WaitGroup
7876

7977
// There's some timing involved in this test, so we'll need
8078
// to retry a few times to cover all the cases.
8179
for i := 0; i < 100; i++ {
82-
cache := New[int, any](Options{MaxEntries: 1000})
80+
cache := New(Options[int, any]{MaxEntries: 1000})
8381
for i := 0; i < 10; i++ {
8482
cache.Set(i, i)
8583
}
@@ -112,12 +110,11 @@ func TestDeleteFunc(t *testing.T) {
112110

113111
wg.Wait()
114112
})
115-
116113
}
117114

118115
func TestGetOrCreate(t *testing.T) {
119116
c := qt.New(t)
120-
cache := New[int, any](Options{MaxEntries: 100})
117+
cache := New(Options[int, any]{MaxEntries: 100})
121118
counter := 0
122119
create := func(key int) (any, error) {
123120
counter++
@@ -136,21 +133,38 @@ func TestGetOrCreate(t *testing.T) {
136133

137134
func TestGetOrCreateError(t *testing.T) {
138135
c := qt.New(t)
139-
cache := New[int, any](Options{MaxEntries: 100})
136+
cache := New(Options[int, any]{MaxEntries: 100})
140137
create := func(key int) (any, error) {
141138
return nil, fmt.Errorf("failed")
142139
}
143140

144141
res, _, err := cache.GetOrCreate(123456, create)
145142
c.Assert(err, qt.ErrorMatches, "failed")
146143
c.Assert(res, qt.IsNil)
144+
}
147145

146+
func TestOnEvict(t *testing.T) {
147+
c := qt.New(t)
148+
var onEvictCalled bool
149+
cache := New(Options[int, any]{MaxEntries: 20, OnEvict: func(key int, value any) {
150+
onEvictCalled = true
151+
}})
152+
153+
create := func(key int) (any, error) {
154+
return key, nil
155+
}
156+
157+
for i := 0; i < 25; i++ {
158+
cache.GetOrCreate(i, create)
159+
}
160+
161+
c.Assert(onEvictCalled, qt.IsTrue)
148162
}
149163

150164
func TestGetOrCreateConcurrent(t *testing.T) {
151165
c := qt.New(t)
152166

153-
cache := New[int, any](Options{MaxEntries: 1000})
167+
cache := New(Options[int, any]{MaxEntries: 1000})
154168

155169
var countersmu sync.Mutex
156170
counters := make(map[int]int)
@@ -206,7 +220,7 @@ func TestGetOrCreateRecursive(t *testing.T) {
206220
n := 200
207221

208222
for i := 0; i < 30; i++ {
209-
cache := New[int, any](Options{MaxEntries: 1000})
223+
cache := New(Options[int, any]{MaxEntries: 1000})
210224

211225
for j := 0; j < 10; j++ {
212226
wg.Add(1)
@@ -251,7 +265,7 @@ func TestGetOrCreateRecursive(t *testing.T) {
251265
func BenchmarkGetOrCreateAndGet(b *testing.B) {
252266
const maxSize = 1000
253267

254-
cache := New[int, any](Options{MaxEntries: maxSize})
268+
cache := New(Options[int, any]{MaxEntries: maxSize})
255269
r := rand.New(rand.NewSource(99))
256270
var mu sync.Mutex
257271
// Partially fill the cache.
@@ -261,7 +275,6 @@ func BenchmarkGetOrCreateAndGet(b *testing.B) {
261275
b.ResetTimer()
262276

263277
b.RunParallel(func(pb *testing.PB) {
264-
265278
b.ResetTimer()
266279
for pb.Next() {
267280
mu.Lock()
@@ -280,7 +293,6 @@ func BenchmarkGetOrCreateAndGet(b *testing.B) {
280293
}
281294
return i2, nil
282295
})
283-
284296
if err != nil {
285297
b.Fatal(err)
286298
}
@@ -290,7 +302,6 @@ func BenchmarkGetOrCreateAndGet(b *testing.B) {
290302
}
291303
}
292304
})
293-
294305
}
295306

296307
func BenchmarkGetOrCreate(b *testing.B) {
@@ -299,7 +310,7 @@ func BenchmarkGetOrCreate(b *testing.B) {
299310
r := rand.New(rand.NewSource(99))
300311
var mu sync.Mutex
301312

302-
cache := New[int, any](Options{MaxEntries: maxSize})
313+
cache := New(Options[int, any]{MaxEntries: maxSize})
303314

304315
// Partially fill the cache.
305316
for i := 0; i < maxSize/3; i++ {
@@ -320,7 +331,6 @@ func BenchmarkGetOrCreate(b *testing.B) {
320331
}
321332
return key, nil
322333
})
323-
324334
if err != nil {
325335
b.Fatal(err)
326336
}
@@ -336,14 +346,14 @@ func BenchmarkCacheSerial(b *testing.B) {
336346
const maxSize = 1000
337347

338348
b.Run("Set", func(b *testing.B) {
339-
cache := New[int, any](Options{MaxEntries: maxSize})
349+
cache := New(Options[int, any]{MaxEntries: maxSize})
340350
for i := 0; i < b.N; i++ {
341351
cache.Set(i, i)
342352
}
343353
})
344354

345355
b.Run("Get", func(b *testing.B) {
346-
cache := New[int, any](Options{MaxEntries: maxSize})
356+
cache := New(Options[int, any]{MaxEntries: maxSize})
347357
numItems := maxSize - 200
348358
for i := 0; i < numItems; i++ {
349359
cache.Set(i, i)
@@ -363,7 +373,7 @@ func BenchmarkCacheParallel(b *testing.B) {
363373
const maxSize = 1000
364374

365375
b.Run("Set", func(b *testing.B) {
366-
cache := New[int, any](Options{MaxEntries: maxSize})
376+
cache := New(Options[int, any]{MaxEntries: maxSize})
367377
var counter uint32
368378
b.RunParallel(func(pb *testing.PB) {
369379
for pb.Next() {
@@ -374,7 +384,7 @@ func BenchmarkCacheParallel(b *testing.B) {
374384
})
375385

376386
b.Run("Get", func(b *testing.B) {
377-
cache := New[int, any](Options{MaxEntries: maxSize})
387+
cache := New(Options[int, any]{MaxEntries: maxSize})
378388
r := rand.New(rand.NewSource(99))
379389
var mu sync.Mutex
380390
numItems := maxSize - 200

0 commit comments

Comments
 (0)