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:
Vendored
+42
-5
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+20
-11
@@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user