diff --git a/cache.go b/cache.go index a9e94f4..4d89a9f 100644 --- a/cache.go +++ b/cache.go @@ -186,6 +186,9 @@ func (c *Cache) doDelete(item *Item) { item.promotions = -2 } else { c.size -= item.size + if c.onDelete != nil { + c.onDelete(item) + } c.list.Remove(item.element) } } diff --git a/cache_test.go b/cache_test.go index 552f36c..8c56189 100644 --- a/cache_test.go +++ b/cache_test.go @@ -23,6 +23,28 @@ func (_ CacheTests) DeletesAValue() { Expect(cache.Get("worm").Value()).To.Equal("sand") } +func (_ CacheTests) OnDeleteCallbackCalled() { + + onDeleteFnCalled := false + onDeleteFn := func(item *Item) { + if item.key == "spice" { + onDeleteFnCalled = true + } + } + + cache := New(Configure().OnDelete(onDeleteFn)) + cache.Set("spice", "flow", time.Minute) + cache.Set("worm", "sand", time.Minute) + + time.Sleep(time.Millisecond * 10) // Run once to init + cache.Delete("spice") + time.Sleep(time.Millisecond * 10) // Wait for worker to pick up deleted items + + Expect(cache.Get("spice")).To.Equal(nil) + Expect(cache.Get("worm").Value()).To.Equal("sand") + Expect(onDeleteFnCalled).To.Equal(true) +} + func (_ CacheTests) FetchesExpiredItems() { cache := New(Configure()) fn := func() (interface{}, error) { return "moo-moo", nil } diff --git a/configuration.go b/configuration.go index a64bb1d..d618215 100644 --- a/configuration.go +++ b/configuration.go @@ -8,6 +8,7 @@ type Configuration struct { promoteBuffer int getsPerPromote int32 tracking bool + onDelete func(item *Item) } // Creates a configuration object with sensible defaults @@ -92,3 +93,11 @@ func (c *Configuration) Track() *Configuration { c.tracking = true return c } + +// OnDelete allows setting a callback function to react to ideam deletion. +// This typically allows to do a cleanup of resources, such as calling a Close() on +// cached object that require some kind of tear-down. +func (c *Configuration) OnDelete(callback func(item *Item)) *Configuration { + c.onDelete = callback + return c +} diff --git a/layeredcache.go b/layeredcache.go index 20b13f9..bb57a3c 100644 --- a/layeredcache.go +++ b/layeredcache.go @@ -195,6 +195,9 @@ func (c *LayeredCache) worker() { item.promotions = -2 } else { c.size -= item.size + if c.onDelete != nil { + c.onDelete(item) + } c.list.Remove(item.element) } } diff --git a/layeredcache_test.go b/layeredcache_test.go index 94b61a6..a9c45fd 100644 --- a/layeredcache_test.go +++ b/layeredcache_test.go @@ -1,10 +1,11 @@ package ccache import ( - . "github.com/karlseguin/expect" "strconv" "testing" "time" + + . "github.com/karlseguin/expect" ) type LayeredCacheTests struct{} @@ -65,6 +66,33 @@ func (_ *LayeredCacheTests) DeletesAValue() { Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima") } +func (_ *LayeredCacheTests) OnDeleteCallbackCalled() { + + onDeleteFnCalled := false + onDeleteFn := func(item *Item) { + + if item.group == "spice" && item.key == "flow" { + onDeleteFnCalled = true + } + } + + cache := Layered(Configure().OnDelete(onDeleteFn)) + cache.Set("spice", "flow", "value-a", time.Minute) + cache.Set("spice", "must", "value-b", time.Minute) + cache.Set("leto", "sister", "ghanima", time.Minute) + + time.Sleep(time.Millisecond * 10) // Run once to init + cache.Delete("spice", "flow") + time.Sleep(time.Millisecond * 10) // Wait for worker to pick up deleted items + + Expect(cache.Get("spice", "flow")).To.Equal(nil) + Expect(cache.Get("spice", "must").Value()).To.Equal("value-b") + Expect(cache.Get("spice", "worm")).To.Equal(nil) + Expect(cache.Get("leto", "sister").Value()).To.Equal("ghanima") + + Expect(onDeleteFnCalled).To.Equal(true) +} + func (_ *LayeredCacheTests) DeletesALayer() { cache := newLayered() cache.Set("spice", "flow", "value-a", time.Minute)