From 9dcadd5f4ae4512af6ce77bbc79dcd15dad3e344 Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Sat, 5 Nov 2016 21:41:57 -0700 Subject: [PATCH] Support cache expiry based on create/last access time --- cache/cache.go | 57 +++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index cc9d37b4..46ce3969 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -19,6 +19,7 @@ package cache import ( "time" + "errors" "fmt" "github.com/platform9/fission" ) @@ -40,8 +41,9 @@ type ( value interface{} } Cache struct { - cache map[interface{}]Value - expiryTime time.Duration + cache map[interface{}]*Value + ctimeExpiry time.Duration + atimeExpiry time.Duration requestChannel chan *request } @@ -53,29 +55,33 @@ type ( } response struct { error - mapCopy map[interface{}]interface{} - value interface{} + existingValue interface{} + mapCopy map[interface{}]interface{} + value interface{} } ) func (c *Cache) IsOld(v *Value) bool { - if c.expiryTime == time.Duration(0) { - return false - } - if time.Now().Sub(v.ctime) > c.expiryTime { + if (c.ctimeExpiry != time.Duration(0)) && (time.Now().Sub(v.ctime) > c.ctimeExpiry) { return true } + + if (c.atimeExpiry != time.Duration(0)) && (time.Now().Sub(v.atime) > c.atimeExpiry) { + return true + } + return false } -func MakeCache(expiryTime time.Duration) *Cache { +func MakeCache(ctimeExpiry, atimeExpiry time.Duration) *Cache { c := &Cache{ - cache: make(map[interface{}]Value), - expiryTime: expiryTime, + cache: make(map[interface{}]*Value), + ctimeExpiry: ctimeExpiry, + atimeExpiry: atimeExpiry, requestChannel: make(chan *request), } go c.service() - if expiryTime != time.Duration(0) { + if ctimeExpiry != time.Duration(0) || atimeExpiry != time.Duration(0) { go c.expiryService() } return c @@ -91,7 +97,7 @@ 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) { + } 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) @@ -104,10 +110,17 @@ func (c *Cache) service() { req.responseChannel <- resp case SET: now := time.Now() - c.cache[req.key] = Value{ - value: req.value, - ctime: now, - atime: now, + if _, ok := c.cache[req.key]; ok { + val := c.cache[req.key] + val.atime = time.Now() + resp.existingValue = val.value + resp.error = errors.New("value already exists") + } else { + c.cache[req.key] = &Value{ + value: req.value, + ctime: now, + atime: now, + } } req.responseChannel <- resp case DELETE: @@ -115,7 +128,7 @@ func (c *Cache) service() { req.responseChannel <- resp case EXPIRE: for k, v := range c.cache { - if c.IsOld(&v) { + if c.IsOld(v) { delete(c.cache, k) } } @@ -123,7 +136,7 @@ func (c *Cache) service() { case COPY: resp.mapCopy = make(map[interface{}]interface{}) for k, v := range c.cache { - resp.mapCopy[k] = v + resp.mapCopy[k] = v.value } req.responseChannel <- resp default: @@ -145,7 +158,9 @@ func (c *Cache) Get(key interface{}) (interface{}, error) { return resp.value, resp.error } -func (c *Cache) Set(key interface{}, value interface{}) error { +// if key exists in the cache, the new value is NOT set; instead an +// error and the old value are returned +func (c *Cache) Set(key interface{}, value interface{}) (error, interface{}) { respChannel := make(chan *response) c.requestChannel <- &request{ requestType: SET, @@ -154,7 +169,7 @@ func (c *Cache) Set(key interface{}, value interface{}) error { responseChannel: respChannel, } resp := <-respChannel - return resp.error + return resp.error, resp.existingValue } func (c *Cache) Delete(key interface{}) error {