Этот коммит содержится в:
Chris
2018-02-28 14:07:11 -06:00
коммит произвёл Christopher Speller
родитель 2fba6fa799
Коммит 600528e1cf
2 изменённых файлов: 43 добавлений и 103 удалений

Просмотреть файл

@@ -9,15 +9,14 @@ package utils
import ( import (
"container/list" "container/list"
"errors"
"sync" "sync"
"time" "time"
) )
// Caching Interface // Caching Interface
type ObjectCache interface { type ObjectCache interface {
AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64) bool AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64)
AddWithDefaultExpires(key, value interface{}) bool AddWithDefaultExpires(key, value interface{})
Purge() Purge()
Get(key interface{}) (value interface{}, ok bool) Get(key interface{}) (value interface{}, ok bool)
Remove(key interface{}) Remove(key interface{})
@@ -32,10 +31,11 @@ type Cache struct {
evictList *list.List evictList *list.List
items map[interface{}]*list.Element items map[interface{}]*list.Element
lock sync.RWMutex lock sync.RWMutex
onEvicted func(key interface{}, value interface{})
name string name string
defaultExpiry int64 defaultExpiry int64
invalidateClusterEvent string invalidateClusterEvent string
currentGeneration int64
len int
} }
// entry is used to hold a value in the evictList // entry is used to hold a value in the evictList
@@ -43,25 +43,16 @@ type entry struct {
key interface{} key interface{}
value interface{} value interface{}
expireAtSecs int64 expireAtSecs int64
generation int64
} }
// New creates an LRU of the given size // New creates an LRU of the given size
func NewLru(size int) *Cache { func NewLru(size int) *Cache {
cache, _ := NewLruWithEvict(size, nil) return &Cache{
return cache
}
func NewLruWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) {
if size <= 0 {
return nil, errors.New(T("utils.iru.with_evict"))
}
c := &Cache{
size: size, size: size,
evictList: list.New(), evictList: list.New(),
items: make(map[interface{}]*list.Element, size), items: make(map[interface{}]*list.Element, size),
onEvicted: onEvicted,
} }
return c, nil
} }
func NewLruWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) *Cache { func NewLruWithParams(size int, name string, defaultExpiry int64, invalidateClusterEvent string) *Cache {
@@ -77,26 +68,19 @@ func (c *Cache) Purge() {
c.lock.Lock() c.lock.Lock()
defer c.lock.Unlock() defer c.lock.Unlock()
if c.onEvicted != nil { c.len = 0
for k, v := range c.items { c.currentGeneration++
c.onEvicted(k, v.Value)
}
}
c.evictList = list.New()
c.items = make(map[interface{}]*list.Element, c.size)
} }
func (c *Cache) Add(key, value interface{}) bool { func (c *Cache) Add(key, value interface{}) {
return c.AddWithExpiresInSecs(key, value, 0) c.AddWithExpiresInSecs(key, value, 0)
} }
func (c *Cache) AddWithDefaultExpires(key, value interface{}) bool { func (c *Cache) AddWithDefaultExpires(key, value interface{}) {
return c.AddWithExpiresInSecs(key, value, c.defaultExpiry) c.AddWithExpiresInSecs(key, value, c.defaultExpiry)
} }
// Add adds a value to the cache. Returns true if an eviction occurred. func (c *Cache) AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64) {
func (c *Cache) AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64) bool {
c.lock.Lock() c.lock.Lock()
defer c.lock.Unlock() defer c.lock.Unlock()
@@ -107,45 +91,46 @@ func (c *Cache) AddWithExpiresInSecs(key, value interface{}, expireAtSecs int64)
// Check for existing item // Check for existing item
if ent, ok := c.items[key]; ok { if ent, ok := c.items[key]; ok {
c.evictList.MoveToFront(ent) c.evictList.MoveToFront(ent)
ent.Value.(*entry).value = value e := ent.Value.(*entry)
ent.Value.(*entry).expireAtSecs = expireAtSecs e.value = value
return false e.expireAtSecs = expireAtSecs
if e.generation != c.currentGeneration {
e.generation = c.currentGeneration
c.len++
}
return
} }
// Add new item // Add new item
ent := &entry{key, value, expireAtSecs} ent := &entry{key, value, expireAtSecs, c.currentGeneration}
entry := c.evictList.PushFront(ent) entry := c.evictList.PushFront(ent)
c.items[key] = entry c.items[key] = entry
c.len++
evict := c.evictList.Len() > c.size if c.evictList.Len() > c.size {
// Verify size not exceeded c.removeElement(c.evictList.Back())
if evict {
c.removeOldest()
} }
return evict
} }
// Get looks up a key's value from the cache.
func (c *Cache) Get(key interface{}) (value interface{}, ok bool) { func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
c.lock.Lock() c.lock.Lock()
defer c.lock.Unlock() defer c.lock.Unlock()
if ent, ok := c.items[key]; ok { if ent, ok := c.items[key]; ok {
e := ent.Value.(*entry)
if ent.Value.(*entry).expireAtSecs > 0 { if e.generation != c.currentGeneration || (e.expireAtSecs > 0 && (time.Now().UnixNano()/int64(time.Second)) > e.expireAtSecs) {
if (time.Now().UnixNano() / int64(time.Second)) > ent.Value.(*entry).expireAtSecs { c.removeElement(ent)
c.removeElement(ent) return nil, false
return nil, false
}
} }
c.evictList.MoveToFront(ent) c.evictList.MoveToFront(ent)
return ent.Value.(*entry).value, true return ent.Value.(*entry).value, true
} }
return
return nil, false
} }
// Remove removes the provided key from the cache.
func (c *Cache) Remove(key interface{}) { func (c *Cache) Remove(key interface{}) {
c.lock.Lock() c.lock.Lock()
defer c.lock.Unlock() defer c.lock.Unlock()
@@ -155,25 +140,19 @@ func (c *Cache) Remove(key interface{}) {
} }
} }
// RemoveOldest removes the oldest item from the cache.
func (c *Cache) RemoveOldest() {
c.lock.Lock()
defer c.lock.Unlock()
c.removeOldest()
}
// Keys returns a slice of the keys in the cache, from oldest to newest. // Keys returns a slice of the keys in the cache, from oldest to newest.
func (c *Cache) Keys() []interface{} { func (c *Cache) Keys() []interface{} {
c.lock.RLock() c.lock.RLock()
defer c.lock.RUnlock() defer c.lock.RUnlock()
keys := make([]interface{}, len(c.items)) keys := make([]interface{}, c.len)
ent := c.evictList.Back()
i := 0 i := 0
for ent != nil { for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
keys[i] = ent.Value.(*entry).key e := ent.Value.(*entry)
ent = ent.Prev() if e.generation == c.currentGeneration {
i++ keys[i] = e.key
i++
}
} }
return keys return keys
@@ -183,7 +162,7 @@ func (c *Cache) Keys() []interface{} {
func (c *Cache) Len() int { func (c *Cache) Len() int {
c.lock.RLock() c.lock.RLock()
defer c.lock.RUnlock() defer c.lock.RUnlock()
return c.evictList.Len() return c.len
} }
func (c *Cache) Name() string { func (c *Cache) Name() string {
@@ -194,20 +173,12 @@ func (c *Cache) GetInvalidateClusterEvent() string {
return c.invalidateClusterEvent return c.invalidateClusterEvent
} }
// removeOldest removes the oldest item from the cache.
func (c *Cache) removeOldest() {
ent := c.evictList.Back()
if ent != nil {
c.removeElement(ent)
}
}
// removeElement is used to remove a given list element from the cache // removeElement is used to remove a given list element from the cache
func (c *Cache) removeElement(e *list.Element) { func (c *Cache) removeElement(e *list.Element) {
c.evictList.Remove(e) c.evictList.Remove(e)
kv := e.Value.(*entry) kv := e.Value.(*entry)
delete(c.items, kv.key) if kv.generation == c.currentGeneration {
if c.onEvicted != nil { c.len--
c.onEvicted(kv.key, kv.value)
} }
delete(c.items, kv.key)
} }

Просмотреть файл

@@ -11,14 +11,7 @@ import "testing"
import "time" import "time"
func TestLRU(t *testing.T) { func TestLRU(t *testing.T) {
evictCounter := 0 l := NewLru(128)
onEvicted := func(k interface{}, v interface{}) {
evictCounter += 1
}
l, err := NewLruWithEvict(128, onEvicted)
if err != nil {
t.Fatalf("err: %v", err)
}
for i := 0; i < 256; i++ { for i := 0; i < 256; i++ {
l.Add(i, i) l.Add(i, i)
@@ -27,10 +20,6 @@ func TestLRU(t *testing.T) {
t.Fatalf("bad len: %v", l.Len()) t.Fatalf("bad len: %v", l.Len())
} }
if evictCounter != 128 {
t.Fatalf("bad evict count: %v", evictCounter)
}
for i, k := range l.Keys() { for i, k := range l.Keys() {
if v, ok := l.Get(k); !ok || v != k || v != i+128 { if v, ok := l.Get(k); !ok || v != k || v != i+128 {
t.Fatalf("bad key: %v", k) t.Fatalf("bad key: %v", k)
@@ -73,26 +62,6 @@ func TestLRU(t *testing.T) {
} }
} }
// test that Add return true/false if an eviction occurred
func TestLRUAdd(t *testing.T) {
evictCounter := 0
onEvicted := func(k interface{}, v interface{}) {
evictCounter += 1
}
l, err := NewLruWithEvict(1, onEvicted)
if err != nil {
t.Fatalf("err: %v", err)
}
if l.Add(1, 1) || evictCounter != 0 {
t.Errorf("should not have an eviction")
}
if !l.Add(2, 2) || evictCounter != 1 {
t.Errorf("should have an eviction")
}
}
func TestLRUExpire(t *testing.T) { func TestLRUExpire(t *testing.T) {
l := NewLru(128) l := NewLru(128)