blob: b2dfeffad979b9d4c0eb8b64d35e3bc579af0889 [file] [log] [blame]
package lib
import (
"fmt"
"io"
"reflect"
"sort"
"time"
"veyron2/ipc"
"veyron2/vdl"
"veyron2/verror"
sample "veyron/examples/wspr_sample"
)
var typedNil []int
// A simple in-memory implementation of a Cache service.
type cacheImpl struct {
cache map[string]vdl.Any
mostRecent sample.KeyValuePair
lastUpdateTime time.Time
}
// NewCached returns a new implementation of the CacheService.
func NewCached() sample.CacheService {
return &cacheImpl{cache: make(map[string]vdl.Any)}
}
// Set sets a value for a key. This should never return an error.
func (c *cacheImpl) Set(_ ipc.ServerContext, key string, value vdl.Any) error {
c.cache[key] = value
c.mostRecent = sample.KeyValuePair{Key: key, Value: value}
c.lastUpdateTime = time.Now()
return nil
}
// Get returns the value for a key. If the key is not in the map, it returns
// an error.
func (c *cacheImpl) Get(_ ipc.ServerContext, key string) (vdl.Any, error) {
if value, ok := c.cache[key]; ok {
return value, nil
}
return typedNil, verror.NotFoundf("key not found: %v", key)
}
// getWithTypeCheck gets the key and tests if its type matches the given time, erroring if it does
// not.
// This exists mostly to shorted the Get* methods below.
func (c *cacheImpl) getWithTypeCheck(key string, rt reflect.Type) (interface{}, error) {
v, err := c.Get(nil, key)
if err != nil {
return reflect.Zero(rt).Interface(), err
}
if !reflect.TypeOf(v).AssignableTo(rt) {
return reflect.Zero(rt).Interface(),
fmt.Errorf("Cannot convert %v (type %v) to type %v", v, reflect.TypeOf(v), rt)
}
return v, nil
}
// Same as Get, but casts the return argument to an int32.
func (c *cacheImpl) GetAsInt32(_ ipc.ServerContext, key string) (int32, error) {
v, err := c.getWithTypeCheck(key, reflect.TypeOf(int32(0)))
return v.(int32), err
}
// Same as Get, but casts the return argument to an int64.
func (c *cacheImpl) GetAsInt64(_ ipc.ServerContext, key string) (int64, error) {
v, err := c.getWithTypeCheck(key, reflect.TypeOf(int64(0)))
return v.(int64), err
}
// Same as Get, but casts the return argument to an uint8.
func (c *cacheImpl) GetAsByte(_ ipc.ServerContext, key string) (byte, error) {
v, err := c.getWithTypeCheck(key, reflect.TypeOf(byte(0)))
return v.(uint8), err
}
// Same as Get, but casts the return argument to an uint32.
func (c *cacheImpl) GetAsUint32(_ ipc.ServerContext, key string) (uint32, error) {
v, err := c.getWithTypeCheck(key, reflect.TypeOf(uint32(0)))
return v.(uint32), err
}
// Same as Get, but casts the return argument to an uint64.
func (c *cacheImpl) GetAsUint64(_ ipc.ServerContext, key string) (uint64, error) {
v, err := c.getWithTypeCheck(key, reflect.TypeOf(uint64(0)))
return v.(uint64), err
}
// Same as Get, but casts the return argument to a float32.
func (c *cacheImpl) GetAsFloat32(_ ipc.ServerContext, key string) (float32, error) {
v, err := c.getWithTypeCheck(key, reflect.TypeOf(float32(0)))
return v.(float32), err
}
// Same as Get, but casts the return argument to a float64.
func (c *cacheImpl) GetAsFloat64(_ ipc.ServerContext, key string) (float64, error) {
v, err := c.getWithTypeCheck(key, reflect.TypeOf(float64(0)))
return v.(float64), err
}
// Same as Get, but casts the return argument to a string.
func (c *cacheImpl) GetAsString(_ ipc.ServerContext, key string) (string, error) {
v, err := c.getWithTypeCheck(key, reflect.TypeOf(""))
return v.(string), err
}
// Same as Get, but casts the return argument to a bool.
func (c *cacheImpl) GetAsBool(_ ipc.ServerContext, key string) (bool, error) {
v, err := c.getWithTypeCheck(key, reflect.TypeOf(false))
return v.(bool), err
}
// Same as Get, but converts the string return argument to an error.
func (c *cacheImpl) GetAsError(_ ipc.ServerContext, key string) (storedError error, callError error) {
v, err := c.getWithTypeCheck(key, reflect.TypeOf([]error{}).Elem())
return v.(error), err
}
// AsMap returns the full contents of the cache as a map.
func (c *cacheImpl) AsMap(ipc.ServerContext) (map[string]vdl.Any, error) {
return c.cache, nil
}
// KeyValuePairs returns the full contents of the cache as a slice of pairs.
func (c *cacheImpl) KeyValuePairs(ipc.ServerContext) ([]sample.KeyValuePair, error) {
kvp := make([]sample.KeyValuePair, 0, len(c.cache))
for key, val := range c.cache {
kvp = append(kvp, sample.KeyValuePair{key, val})
}
return kvp, nil
}
// MostRecentSet returns the key and value and the timestamp for the most
// recent set operation
// TODO(bprosnitz) support type types and change time to native time type
func (c *cacheImpl) MostRecentSet(ipc.ServerContext) (sample.KeyValuePair, int64, error) {
var err error
if c.lastUpdateTime.IsZero() {
err = verror.NotFoundf("no values in the cache so cannot return most recent.")
}
return c.mostRecent, c.lastUpdateTime.Unix(), err
}
// KeyPage indexes into the keys (in alphanumerically sorted order) and
// returns the indexth page of 10 keys.
func (c *cacheImpl) KeyPage(_ ipc.ServerContext, index int64) ([10]string, error) {
results := [10]string{}
keys := sort.StringSlice{}
for key, _ := range c.cache {
keys = append(keys, key)
}
keys.Sort()
lowIndex := int(index) * 10
if index < 0 || len(keys) <= lowIndex {
return results, verror.BadArgf("Page index out of bounds: %d", index)
}
highIndex := lowIndex + 9
if highIndex > len(keys)-1 {
highIndex = len(keys) - 1
}
for i := 0; lowIndex+i <= highIndex; i++ {
results[i] = keys[lowIndex+i]
}
return results, nil
}
// Size returns the total number of entries in the cache.
func (c *cacheImpl) Size(ipc.ServerContext) (int64, error) {
return int64(len(c.cache)), nil
}
// MultiGet handles a stream of get requests. Returns an error if one of the
// keys in the stream is not in the map or if there was an issue reading
// the stream.
func (c *cacheImpl) MultiGet(_ ipc.ServerContext, stream sample.CacheServiceMultiGetStream) error {
for {
key, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
value, ok := c.cache[key]
if !ok {
return fmt.Errorf("key not found: %v", key)
}
stream.Send(value)
}
}