Add expiry time to cache

MakeCache() now takes a time.Duration after which entries are
considered invalid, and not returned from a Get().
This commit is contained in:
Soam Vasani
2016-11-04 23:05:29 -07:00
parent f639bc3558
commit 094da5c69a
2 changed files with 62 additions and 16 deletions
+42 -5
View File
@@ -29,6 +29,7 @@ const (
GET requestType = iota GET requestType = iota
SET SET
DELETE DELETE
EXPIRE
) )
type ( type (
@@ -39,6 +40,7 @@ type (
} }
Cache struct { Cache struct {
cache map[interface{}]Value cache map[interface{}]Value
expiryTime time.Duration
requestChannel chan *request 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{ c := &Cache{
cache: make(map[interface{}]Value), cache: make(map[interface{}]Value),
expiryTime: expiryTime,
requestChannel: make(chan *request), requestChannel: make(chan *request),
} }
go c.service() go c.service()
if expiryTime != time.Duration(0) {
go c.expiryService()
}
return c return c
} }
@@ -73,11 +89,16 @@ func (c *Cache) service() {
if !ok { if !ok {
resp.error = fission.MakeError(fission.ErrorNotFound, resp.error = fission.MakeError(fission.ErrorNotFound,
fmt.Sprintf("key '%v' not found", req.key)) 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 req.responseChannel <- resp
case SET: case SET:
now := time.Now() now := time.Now()
@@ -90,6 +111,13 @@ func (c *Cache) service() {
case DELETE: case DELETE:
delete(c.cache, req.key) delete(c.cache, req.key)
req.responseChannel <- resp req.responseChannel <- resp
case EXPIRE:
for k, v := range c.cache {
if c.IsOld(&v) {
delete(c.cache, k)
}
}
// no response
default: default:
resp.error = fission.MakeError(fission.ErrorInvalidArgument, resp.error = fission.MakeError(fission.ErrorInvalidArgument,
fmt.Sprintf("invalid request type: %v", req.requestType)) fmt.Sprintf("invalid request type: %v", req.requestType))
@@ -131,3 +159,12 @@ func (c *Cache) Delete(key interface{}) error {
resp := <-respChannel resp := <-respChannel
return resp.error return resp.error
} }
func (c *Cache) expiryService() {
for {
time.Sleep(time.Minute)
c.requestChannel <- &request{
requestType: EXPIRE,
}
}
}
+20 -11
View File
@@ -18,30 +18,39 @@ package cache
import "testing" import "testing"
import "log" import "log"
import "time"
func checkErr(err error) {
if err != nil {
log.Panicf("err: %v", err)
}
}
func TestCache(t *testing.T) { func TestCache(t *testing.T) {
c := MakeCache() c := MakeCache(100 * time.Millisecond)
err := c.Set("a", "b") err := c.Set("a", "b")
if err != nil { checkErr(err)
log.Panicf("error: %v", err)
}
val, err := c.Get("a") val, err := c.Get("a")
if err != nil { checkErr(err)
log.Panicf("error: %v", err)
}
if val != "b" { if val != "b" {
log.Panicf("value %v", val) log.Panicf("value %v", val)
} }
err = c.Delete("a") err = c.Delete("a")
if err != nil { checkErr(err)
log.Panicf("error: %v", err)
}
_, err = c.Get("a") _, err = c.Get("a")
if err == nil { 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")
} }
} }