Delete expired items before setting

Check if an item exists and it has expired before setting so that
onEvicted is actually called.

Fixes #48.

Add test and split func

Use nanoseconds instead

Call onEvicted if item existed before setting

Call onEvicted if item.Expired
This commit is contained in:
Jaime Martinez 2020-10-13 11:33:45 +11:00
parent 46f4078530
commit 38560f59f4
No known key found for this signature in database
GPG Key ID: 7A88010914F01A73
2 changed files with 46 additions and 0 deletions

View File

@ -57,7 +57,15 @@ func (c *cache) Set(k string, x interface{}, d time.Duration) {
if d > 0 { if d > 0 {
e = time.Now().Add(d).UnixNano() e = time.Now().Add(d).UnixNano()
} }
var item Item
var evicted bool
c.mu.Lock() c.mu.Lock()
if c.onEvicted != nil {
item, evicted = c.items[k]
}
c.items[k] = Item{ c.items[k] = Item{
Object: x, Object: x,
Expiration: e, 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 // TODO: Calls to mu.Unlock are currently not deferred because defer
// adds ~200 ns (as of go1.) // adds ~200 ns (as of go1.)
c.mu.Unlock() 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) { func (c *cache) set(k string, x interface{}, d time.Duration) {

View File

@ -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) { func TestCacheSerialization(t *testing.T) {
tc := New(DefaultExpiration, 0) tc := New(DefaultExpiration, 0)
testFillAndSerialize(t, tc) testFillAndSerialize(t, tc)