Skip to content

Commit b0fa077

Browse files
committed
chore: expose gc api
1 parent 9f441f7 commit b0fa077

5 files changed

Lines changed: 90 additions & 34 deletions

File tree

arc/arc.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,21 @@ func (a *arc) Notify(fn func(libcache.Event), ops ...libcache.Op) {
184184
a.t2.Notify(fn, ops...)
185185
}
186186

187+
func (a *arc) GC() time.Duration {
188+
x := a.t1.GC()
189+
y := a.t2.GC()
190+
191+
// return the next nearer gc cycle.
192+
if y == 0 {
193+
return x
194+
} else if x == 0 {
195+
return y
196+
} else if x < y {
197+
return x
198+
}
199+
return y
200+
}
201+
187202
func min(x, y int) int {
188203
if x < y {
189204
return x

cache.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,18 @@ type Cache interface {
6363
//
6464
// Deprecated: use Notify instead.
6565
RegisterOnExpired(f func(key, value interface{}))
66-
6766
// Notify causes cahce to relay events to fn.
6867
// If no operations are provided, all incoming operations will be relayed to fn.
6968
// Otherwise, just the provided operations will.
7069
Notify(fn func(Event), ops ...Op)
70+
// GC runs a garbage collection and blocks the caller until the
71+
// all expired items from the cache evicted.
72+
//
73+
// GC returns the remaining time duration for the next gc cycle
74+
// if there any, Otherwise, it return 0.
75+
//
76+
// Calling GC without waits for the duration to elapsed considered a no-op.
77+
GC() time.Duration
7178
}
7279

7380
type cache struct {
@@ -194,3 +201,10 @@ func (c *cache) Expiry(key interface{}) (time.Time, bool) {
194201
c.mu.Unlock()
195202
return exp, ok
196203
}
204+
205+
func (c *cache) GC() time.Duration {
206+
c.mu.Lock()
207+
dur := c.unsafe.GC()
208+
c.mu.Unlock()
209+
return dur
210+
}

cache_test.go

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -309,32 +309,6 @@ func TestExpiring(t *testing.T) {
309309
}
310310
}
311311

312-
func BenchmarkCache(b *testing.B) {
313-
for _, tt := range cacheTests {
314-
b.Run("Benchmark"+tt.cont.String()+"Cache", func(b *testing.B) {
315-
keys := []interface{}{}
316-
cache := tt.cont.New(0)
317-
318-
for i := 0; i < 100; i++ {
319-
keys = append(keys, i)
320-
}
321-
322-
b.ResetTimer()
323-
b.RunParallel(func(pb *testing.PB) {
324-
for pb.Next() {
325-
key := keys[rand.Intn(100)]
326-
_, ok := cache.Load(key)
327-
if ok {
328-
cache.Delete(key)
329-
} else {
330-
cache.Store(key, struct{}{})
331-
}
332-
}
333-
})
334-
})
335-
}
336-
}
337-
338312
func TestNotify(t *testing.T) {
339313
for _, tt := range cacheTests {
340314
t.Run("Test"+tt.cont.String()+"CacheNotify", func(t *testing.T) {
@@ -359,5 +333,46 @@ func TestNotify(t *testing.T) {
359333
}
360334
})
361335
}
336+
}
337+
338+
func TestGC(t *testing.T) {
339+
for _, tt := range cacheTests {
340+
t.Run("Test"+tt.cont.String()+"CacheGC", func(t *testing.T) {
341+
cache := tt.cont.NewUnsafe(0)
342+
cache.StoreWithTTL(0, 0, time.Nanosecond)
343+
cache.StoreWithTTL(1, 1, time.Millisecond*100)
344+
dur := cache.GC()
345+
346+
assert.GreaterOrEqual(t, int64(dur), int64(time.Millisecond*99))
347+
time.Sleep(dur)
348+
349+
assert.Zero(t, int(cache.GC()))
350+
assert.Zero(t, cache.Len())
351+
})
352+
}
353+
}
354+
func BenchmarkCache(b *testing.B) {
355+
for _, tt := range cacheTests {
356+
b.Run("Benchmark"+tt.cont.String()+"Cache", func(b *testing.B) {
357+
keys := []interface{}{}
358+
cache := tt.cont.New(0)
359+
360+
for i := 0; i < 100; i++ {
361+
keys = append(keys, i)
362+
}
362363

364+
b.ResetTimer()
365+
b.RunParallel(func(pb *testing.PB) {
366+
for pb.Next() {
367+
key := keys[rand.Intn(100)]
368+
_, ok := cache.Load(key)
369+
if ok {
370+
cache.Delete(key)
371+
} else {
372+
cache.Store(key, struct{}{})
373+
}
374+
}
375+
})
376+
})
377+
}
363378
}

idle/idle.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func (idle) Len() (len int) { return }
2727
func (idle) Cap() (cap int) { return }
2828
func (idle) TTL() (t time.Duration) { return }
2929
func (idle) Expiry(interface{}) (t time.Time, ok bool) { return }
30+
func (idle) GC() (dur time.Duration) { return }
3031
func (idle) Update(interface{}, interface{}) {}
3132
func (idle) Store(interface{}, interface{}) {}
3233
func (idle) StoreWithTTL(interface{}, interface{}, time.Duration) {}

internal/cache.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func (c *Cache) Peek(key interface{}) (interface{}, bool) {
9494

9595
func (c *Cache) get(key interface{}, peek bool) (interface{}, bool) {
9696
// Run GC inline before return the entry.
97-
c.gc()
97+
c.GC()
9898

9999
e, ok := c.entries[key]
100100
if !ok {
@@ -127,7 +127,7 @@ func (c *Cache) Store(key, value interface{}) {
127127
// StoreWithTTL sets the key value with TTL overrides the default.
128128
func (c *Cache) StoreWithTTL(key, value interface{}, ttl time.Duration) {
129129
// Run GC inline before pushing the new entry.
130-
c.gc()
130+
c.GC()
131131

132132
if e, ok := c.entries[key]; ok {
133133
c.removeEntry(e)
@@ -152,7 +152,7 @@ func (c *Cache) StoreWithTTL(key, value interface{}, ttl time.Duration) {
152152
// Update the key value without updating the underlying "rank".
153153
func (c *Cache) Update(key, value interface{}) {
154154
// Run GC inline before update the entry.
155-
c.gc()
155+
c.GC()
156156

157157
if c.Contains(key) {
158158
e := c.entries[key]
@@ -265,14 +265,25 @@ func (c *Cache) emit(op Op, k, v interface{}, exp time.Time, ok bool) {
265265
}
266266
}
267267

268-
func (c *Cache) gc() {
268+
// GC returns the remaining time duration for the next gc cycle if there any,
269+
// Otherwise, it return 0.
270+
//
271+
// Calling GC without waits for the duration to elapsed considered a no-op.
272+
func (c *Cache) GC() time.Duration {
269273
now := time.Now()
270274
for {
275+
271276
// Return from gc if the heap is empty or the next element is not yet
272-
// expired
273-
if len(c.heap) == 0 || now.Before(c.heap[0].Exp) {
274-
return
277+
// expired.
278+
279+
if len(c.heap) == 0 {
280+
return 0
275281
}
282+
283+
if now.Before(c.heap[0].Exp) {
284+
return c.heap[0].Exp.Sub(now)
285+
}
286+
276287
e := heap.Pop(&c.heap).(*Entry)
277288
c.evict(e)
278289
}

0 commit comments

Comments
 (0)