From 1c32ce19fef6e9ee61429ccddf999fe01e4b292b Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Sun, 30 Oct 2016 00:02:43 -0700 Subject: [PATCH] Cache -- simple threadsafe map --- cache/cache.go | 133 ++++++++++++++++++++++++++++++++++++++++++++ cache/cache_test.go | 47 ++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 cache/cache.go create mode 100644 cache/cache_test.go diff --git a/cache/cache.go b/cache/cache.go new file mode 100644 index 00000000..87c24f6d --- /dev/null +++ b/cache/cache.go @@ -0,0 +1,133 @@ +/* +Copyright 2016 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cache + +import ( + "time" + + "fmt" + "github.com/platform9/fission" +) + +type requestType int + +const ( + GET requestType = iota + SET + DELETE +) + +type ( + Value struct { + ctime time.Time + atime time.Time + value interface{} + } + Cache struct { + cache map[interface{}]Value + requestChannel chan *request + } + + request struct { + requestType + key interface{} + value interface{} + responseChannel chan *response + } + response struct { + error + value interface{} + } +) + +func MakeCache() *Cache { + c := &Cache{ + cache: make(map[interface{}]Value), + requestChannel: make(chan *request), + } + go c.service() + return c +} + +func (c *Cache) service() { + for { + req := <-c.requestChannel + resp := &response{} + switch req.requestType { + case GET: + val, ok := c.cache[req.key] + if !ok { + resp.error = fission.MakeError(fission.ErrorNotFound, + fmt.Sprintf("key '%v' not found", req.key)) + } + val.atime = time.Now() + c.cache[req.key] = val + + resp.value = val.value + req.responseChannel <- resp + case SET: + now := time.Now() + c.cache[req.key] = Value{ + value: req.value, + ctime: now, + atime: now, + } + req.responseChannel <- resp + case DELETE: + delete(c.cache, req.key) + req.responseChannel <- resp + default: + resp.error = fission.MakeError(fission.ErrorInvalidArgument, + fmt.Sprintf("invalid request type: %v", req.requestType)) + req.responseChannel <- resp + } + } +} + +func (c *Cache) Get(key interface{}) (interface{}, error) { + respChannel := make(chan *response) + c.requestChannel <- &request{ + requestType: GET, + key: key, + responseChannel: respChannel, + } + resp := <-respChannel + return resp.value, resp.error +} + +func (c *Cache) Set(key interface{}, value interface{}) error { + respChannel := make(chan *response) + c.requestChannel <- &request{ + requestType: SET, + key: key, + value: value, + responseChannel: respChannel, + } + resp := <-respChannel + return resp.error +} + +func (c *Cache) Delete(key interface{}) error { + respChannel := make(chan *response) + c.requestChannel <- &request{ + requestType: DELETE, + key: key, + responseChannel: respChannel, + } + resp := <-respChannel + return resp.error +} diff --git a/cache/cache_test.go b/cache/cache_test.go new file mode 100644 index 00000000..52ba9657 --- /dev/null +++ b/cache/cache_test.go @@ -0,0 +1,47 @@ +/* +Copyright 2016 The Fission Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cache + +import "testing" +import "log" + +func TestCache(t *testing.T) { + c := MakeCache() + + err := c.Set("a", "b") + if err != nil { + log.Panicf("error: %v", err) + } + + val, err := c.Get("a") + if err != nil { + log.Panicf("error: %v", err) + } + if val != "b" { + log.Panicf("value %v", val) + } + + err = c.Delete("a") + if err != nil { + log.Panicf("error: %v", err) + } + + _, err = c.Get("a") + if err == nil { + log.Panicf("error: %v", err) + } +}