diff --git a/cache.go b/cache.go index db88d2f..43f4e58 100644 --- a/cache.go +++ b/cache.go @@ -57,7 +57,15 @@ func (c *cache) Set(k string, x interface{}, d time.Duration) { if d > 0 { e = time.Now().Add(d).UnixNano() } + + var item Item + var evicted bool + c.mu.Lock() + if c.onEvicted != nil { + item, evicted = c.items[k] + } + c.items[k] = Item{ Object: x, Expiration: e, @@ -65,6 +73,11 @@ func (c *cache) Set(k string, x interface{}, d time.Duration) { // TODO: Calls to mu.Unlock are currently not deferred because defer // adds ~200 ns (as of go1.) c.mu.Unlock() + + // try to call onEvicted if key existed before but it was expired before cleanup + if evicted && item.Expired() { + c.onEvicted(k, item.Object) + } } func (c *cache) set(k string, x interface{}, d time.Duration) { diff --git a/cache_test.go b/cache_test.go index de3e9d6..af7fff2 100644 --- a/cache_test.go +++ b/cache_test.go @@ -1247,6 +1247,39 @@ func TestOnEvicted(t *testing.T) { } } +func TestOnEvictedCalledBeforeSet(t *testing.T) { + tc := New(DefaultExpiration, 0) + expiry := 1 * time.Nanosecond + + works := false + tc.OnEvicted(func(k string, v interface{}) { + if k == "foo" && v.(int) == 3 { + + works = true + } + tc.Set("bar", 4, DefaultExpiration) + }) + + tc.Set("foo", 3, expiry) + if tc.onEvicted == nil { + t.Fatal("tc.onEvicted is nil") + } + + // ensure item expires + time.Sleep(expiry) + + // calling Set again should evict expired item + tc.Set("foo", 3, DefaultExpiration) + + x, _ := tc.Get("bar") + if !works { + t.Fatal("works bool not true") + } + if x.(int) != 4 { + t.Error("bar was not 4") + } +} + func TestCacheSerialization(t *testing.T) { tc := New(DefaultExpiration, 0) testFillAndSerialize(t, tc)