From 094da5c69a6ca7587243d21190b4c7bf53efde90 Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Thu, 3 Nov 2016 22:12:34 -0700 Subject: [PATCH] Add expiry time to cache MakeCache() now takes a time.Duration after which entries are considered invalid, and not returned from a Get(). --- cache/cache.go | 47 ++++++++++++++++++++++++++++++++++++++++----- cache/cache_test.go | 31 +++++++++++++++++++----------- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index 87c24f6d..c327c3fa 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -29,6 +29,7 @@ const ( GET requestType = iota SET DELETE + EXPIRE ) type ( @@ -39,6 +40,7 @@ type ( } Cache struct { cache map[interface{}]Value + expiryTime time.Duration requestChannel chan *request } @@ -54,12 +56,26 @@ type ( } ) -func MakeCache() *Cache { +func (c *Cache) IsOld(v *Value) bool { + if c.expiryTime == 0 { + return false + } + if time.Now().Sub(v.atime) > c.expiryTime { + return true + } + return false +} + +func MakeCache(expiryTime time.Duration) *Cache { c := &Cache{ cache: make(map[interface{}]Value), + expiryTime: expiryTime, requestChannel: make(chan *request), } go c.service() + if expiryTime != time.Duration(0) { + go c.expiryService() + } return c } @@ -73,11 +89,16 @@ func (c *Cache) service() { if !ok { resp.error = fission.MakeError(fission.ErrorNotFound, fmt.Sprintf("key '%v' not found", req.key)) + } else if c.IsOld(&val) { + resp.error = fission.MakeError(fission.ErrorNotFound, + fmt.Sprintf("key '%v' expired (atime %v)", req.key, val.atime)) + delete(c.cache, req.key) + } else { + // update atime + val.atime = time.Now() + c.cache[req.key] = val + resp.value = val.value } - val.atime = time.Now() - c.cache[req.key] = val - - resp.value = val.value req.responseChannel <- resp case SET: now := time.Now() @@ -90,6 +111,13 @@ func (c *Cache) service() { case DELETE: delete(c.cache, req.key) req.responseChannel <- resp + case EXPIRE: + for k, v := range c.cache { + if c.IsOld(&v) { + delete(c.cache, k) + } + } + // no response default: resp.error = fission.MakeError(fission.ErrorInvalidArgument, fmt.Sprintf("invalid request type: %v", req.requestType)) @@ -131,3 +159,12 @@ func (c *Cache) Delete(key interface{}) error { resp := <-respChannel return resp.error } + +func (c *Cache) expiryService() { + for { + time.Sleep(time.Minute) + c.requestChannel <- &request{ + requestType: EXPIRE, + } + } +} diff --git a/cache/cache_test.go b/cache/cache_test.go index 52ba9657..fc4baa36 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -18,30 +18,39 @@ package cache import "testing" import "log" +import "time" + +func checkErr(err error) { + if err != nil { + log.Panicf("err: %v", err) + } +} func TestCache(t *testing.T) { - c := MakeCache() + c := MakeCache(100 * time.Millisecond) err := c.Set("a", "b") - if err != nil { - log.Panicf("error: %v", err) - } + checkErr(err) val, err := c.Get("a") - if err != nil { - log.Panicf("error: %v", err) - } + checkErr(err) if val != "b" { log.Panicf("value %v", val) } err = c.Delete("a") - if err != nil { - log.Panicf("error: %v", err) - } + checkErr(err) _, err = c.Get("a") if err == nil { - log.Panicf("error: %v", err) + log.Panicf("found deleted element") + } + + err = c.Set("expires", "42") + checkErr(err) + time.Sleep(150 * time.Millisecond) + _, err = c.Get("expires") + if err == nil { + log.Panicf("found expired element") } }