blob: 7d670e89bc5c5b4759f597e012e48f7d41902f53 [file] [log] [blame]
package lib
import (
"bytes"
"crypto/md5"
"encoding/base64"
"fmt"
"log"
"sync"
"veyron/runtimes/google/lib/lru"
"veyron2/ipc"
"veyron2/security"
"veyron2/vom"
)
// ClientCache is a concurrent-use, type-safe wrapper over lru.Cache
// where keys are (hashes of) identity.PrivateID and values are
// ipc.Client.
type ClientCache struct {
sync.Mutex
// TODO(bjornick): Write our own lru cache that doesn't remove an entry
// on get.
cache *lru.Cache
nPuts, nGets, nHits, nEvicts int64
}
// NewClientCache returns a cache with a total of size entries.
func NewClientCache(size int) *ClientCache {
return &ClientCache{cache: lru.New(size)}
}
// Put stores the client for the passed in identity.
func (c *ClientCache) Put(id security.PrivateID, client ipc.Client) {
key, err := c.key(id)
if err != nil {
log.Println("ERROR creating cache key for identity:", id, ", error:", err)
return
}
c.Lock()
c.nPuts++
_, evictedValue, evicted := c.cache.Put(key, client)
if evicted {
c.nEvicts++
}
c.Unlock()
if evicted {
evictedValue.(ipc.Client).Close()
}
}
// Get returns the cached client for id. If there is no entry, it returns nil.
func (c *ClientCache) Get(id security.PrivateID) ipc.Client {
key, err := c.key(id)
if err != nil {
log.Println("ERROR creating cache key for identity:", id, ", error:", err)
return nil
}
c.Lock()
defer c.Unlock()
c.nGets++
if v, ok := c.cache.Get(key); ok {
c.nHits++
c.cache.Put(key, v)
return v.(ipc.Client)
}
return nil
}
func (c *ClientCache) key(id security.PrivateID) (string, error) {
if id == nil {
return "", nil
}
var buf bytes.Buffer
b64 := base64.NewEncoder(base64.URLEncoding, &buf)
if err := vom.NewEncoder(b64).Encode(id.PublicID()); err != nil {
return "", err
}
b64.Close()
h := md5.New()
h.Write(buf.Bytes())
return string(h.Sum(nil)), nil
}
// Stats returns a debug string contain performance stats for the cache.
func (c *ClientCache) Stats() string {
c.Lock()
defer c.Unlock()
hitRate := int64(0)
if c.nGets > 0 {
hitRate = c.nHits * 100 / c.nGets
}
return fmt.Sprintf("Size:%d Put:%d Get:%d Hits:%d (%d%%) Evicts:%d", c.cache.Size(), c.nPuts, c.nGets, c.nHits, hitRate, c.nEvicts)
}