sm
/
cache
1
0
Fork 0

fixed few bugs

This commit is contained in:
Giuseppe 2015-12-05 14:19:03 +01:00
parent 4c8dd8263a
commit 97de98b0f1
2 changed files with 73 additions and 29 deletions

View File

@ -15,11 +15,15 @@ import (
type Item struct { type Item struct {
Object interface{} Object interface{}
Expiration int64 Expiration int64
}
type Node struct {
Expiration int64
Key string Key string
} }
func (item Item) Less(than llrb.Item) bool { func (node Node) Less(than llrb.Item) bool {
return item.Expiration < than.(Item).Expiration return node.Expiration < than.(Node).Expiration
} }
@ -66,23 +70,41 @@ func (c *cache) Set(k string, x interface{}, d time.Duration) {
} }
func (c *cache) set(k string, x interface{}, d time.Duration) { func (c *cache) set(k string, x interface{}, d time.Duration) {
item := Item{ item := Item{Object: x,}
Object: x,
Key : k,
}
if d == DefaultExpiration { if d == DefaultExpiration {
d = c.defaultExpiration d = c.defaultExpiration
} }
if d > 0 { if d > 0 {
item.Expiration = time.Now().Add(d).UnixNano() item.Expiration = time.Now().Add(d).UnixNano()
_, found := c.items[k] old, found := c.items[k]
if !found { if found && old.Expiration != item.Expiration{
c.sortedItems.InsertNoReplace(item) c.deleteFromBst(Node{Expiration: old.Expiration, Key: k})
c.sortedItems.InsertNoReplace(Node{Expiration: item.Expiration, Key: k})
} else if !found {
c.sortedItems.InsertNoReplace(Node{Expiration: item.Expiration, Key: k})
} }
} }
c.items[k] = item c.items[k] = item
} }
func (c *cache) deleteFromBst (node Node) {
//delete nodes from the tree with the same expiration
//until the required one is found
var toReinsert []Node
for del := c.sortedItems.Delete(node); del != nil; {
delNode := del.(Node)
if delNode.Key == node.Key {
break
} else {
toReinsert = append (toReinsert, delNode)
}
}
//reinsert the nodes in the tree, with modified expiration
for _, delNode := range toReinsert {
c.sortedItems.InsertNoReplace(delNode)
}
}
// Add an item to the cache only if an item doesn't already exist for the given // Add an item to the cache only if an item doesn't already exist for the given
// key, or if the existing item has expired. Returns an error otherwise. // key, or if the existing item has expired. Returns an error otherwise.
@ -880,34 +902,49 @@ func (c *cache) Delete(k string) {
} }
func (c *cache) delete(k string) (interface{}, bool) { func (c *cache) delete(k string) (interface{}, bool) {
if c.onEvicted != nil { if v, found := c.items[k]; found {
if v, found := c.items[k]; found { delete(c.items, k)
delete(c.items, k) c.deleteFromBst(Node{Expiration: v.Expiration, Key: k})
if c.onEvicted != nil {
return v.Object, true return v.Object, true
} else {
return nil, false
} }
} }
delete(c.items, k)
return nil, false return nil, false
} }
type keyAndValue struct {
key string
value interface{}
}
// Delete all expired items from the cache. // Delete all expired items from the cache.
func (c *cache) DeleteExpired() { func (c *cache) DeleteExpired() {
var evictedItems []Item var evictedNodes []Node
var evictedItems []keyAndValue
c.mu.Lock() c.mu.Lock()
c.sortedItems.DescendLessOrEqual(Item{Expiration: time.Now().UnixNano()}, func(i llrb.Item) bool { c.sortedItems.DescendLessOrEqual(Node{Expiration: time.Now().UnixNano()}, func(i llrb.Item) bool {
v := i.(Item) v := i.(Node)
c.delete(v.Key) k := v.Key
evictedItems = append(evictedItems, v) item, found := c.items[k]
if !found {
panic("Item in tree but not in map!!")
}
delete(c.items, k)
evictedItems = append(evictedItems, keyAndValue{k, item.Object})
evictedNodes = append(evictedNodes, v)
return true return true
}) })
for _, v := range evictedItems { for _, v := range evictedNodes {
c.sortedItems.Delete(v) c.sortedItems.Delete(v)
} }
c.mu.Unlock() c.mu.Unlock()
for _, v := range evictedItems { if c.onEvicted != nil {
if c.onEvicted != nil { for _, n := range evictedItems {
c.onEvicted(v.Key, v.Object) c.onEvicted(n.key, n.value)
} }
} }
} }
@ -976,10 +1013,10 @@ func (c *cache) Load(r io.Reader) error {
ov, found := c.items[k] ov, found := c.items[k]
if !found || ov.Expired() { if !found || ov.Expired() {
c.items[k] = v c.items[k] = v
if found { if !found {
c.sortedItems.Delete(ov) c.sortedItems.InsertNoReplace(Node{Expiration: v.Expiration, Key: k})
} }
c.sortedItems.InsertNoReplace(v)
} }
} }
@ -1073,9 +1110,8 @@ func newCache(de time.Duration, m map[string]Item) *cache {
items: m, items: m,
} }
c.sortedItems = llrb.New() c.sortedItems = llrb.New()
//we can probably do bulk insertion here to speed it up for k, item := range m {
for _, item := range m { c.sortedItems.InsertNoReplace(Node{Key: k, Expiration: item.Expiration})
c.sortedItems.InsertNoReplace(item)
} }
return c return c
} }

View File

@ -1680,5 +1680,13 @@ func benchmarkLargeCache(b *testing.B, nano int) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
tc.Set(strconv.Itoa(i), "bar", DefaultExpiration) tc.Set(strconv.Itoa(i), "bar", DefaultExpiration)
} }
b.StopTimer()
tc.DeleteExpired()
now := time.Now().UnixNano()
for _, item := range tc.Items() {
if item.Expiration < now {
b.Fatalf("some items have not been correctly evicted")
}
}
} }