Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

WIP: cache: Change cache implementation from FIFO to LRU #494

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 55 additions & 36 deletions plumbing/cache/object.go
Original file line number Diff line number Diff line change
@@ -1,68 +1,87 @@
package cache

import "gopkg.in/src-d/go-git.v4/plumbing"
import (
"github.com/golang/groupcache/lru"
"gopkg.in/src-d/go-git.v4/plumbing"
)

const (
initialQueueSize = 20
MaxSize = 10 * MiByte
MaxSize = 96 * MiByte
)

type ObjectFIFO struct {
objects map[plumbing.Hash]plumbing.EncodedObject
order *queue
type ObjectLRU struct {
// Max file size allowed into this cache
MaxSize FileSize

maxSize FileSize
cache *lru.Cache
actualSize FileSize
}

// NewObjectFIFO returns an Object cache that keeps the newest objects that fit
// NewObjectLRU returns an Object cache that keeps the most used objects that fit
// into the specific memory size
func NewObjectFIFO(size FileSize) *ObjectFIFO {
return &ObjectFIFO{
objects: make(map[plumbing.Hash]plumbing.EncodedObject),
order: newQueue(initialQueueSize),
maxSize: size,
func NewObjectLRU(size FileSize) *ObjectLRU {
olru := &ObjectLRU{
MaxSize: size,
}

lc := lru.New(0)
lc.OnEvicted = func(key lru.Key, value interface{}) {
obj := getAsObject(value)
olru.actualSize -= FileSize(obj.Size())
}
olru.cache = lc

return olru
}

func getAsObject(value interface{}) plumbing.EncodedObject {
v, ok := value.(plumbing.EncodedObject)
if !ok {
panic("unreachable")
}

return v
}

// Add adds a new object to the cache. If the object size is greater than the
// cache size, the object is not added.
func (c *ObjectFIFO) Add(o plumbing.EncodedObject) {
// cache size, the less used objects will be discarded until the cache fits into
// the specified size again.
func (c *ObjectLRU) Add(o plumbing.EncodedObject) {
// if the size of the object is bigger or equal than the cache size,
// skip it
if FileSize(o.Size()) >= c.maxSize {
if FileSize(o.Size()) >= c.MaxSize {
return
}

// if the object is into the cache, do not add it again
if _, ok := c.objects[o.Hash()]; ok {
return
oldLen := c.cache.Len()
c.cache.Add(o.Hash(), o)
if oldLen < c.cache.Len() {
c.actualSize += FileSize(o.Size())
}

// delete the oldest object if cache is full
if c.actualSize >= c.maxSize {
h := c.order.Pop()
o := c.objects[h]
if o != nil {
c.actualSize -= FileSize(o.Size())
delete(c.objects, h)
if c.actualSize > c.MaxSize {
for {
if c.actualSize <= c.MaxSize {
break
}

c.cache.RemoveOldest()
}
}

c.objects[o.Hash()] = o
c.order.Push(o.Hash())
c.actualSize += FileSize(o.Size())
}

// Get returns an object by his hash. If the object is not found in the cache, it
// returns nil
func (c *ObjectFIFO) Get(k plumbing.Hash) plumbing.EncodedObject {
return c.objects[k]
func (c *ObjectLRU) Get(k plumbing.Hash) plumbing.EncodedObject {
val, ok := c.cache.Get(k)
if !ok {
return nil
}

return getAsObject(val)
}

// Clear the content of this object cache
func (c *ObjectFIFO) Clear() {
c.objects = make(map[plumbing.Hash]plumbing.EncodedObject)
c.order = newQueue(initialQueueSize)
c.actualSize = 0
func (c *ObjectLRU) Clear() {
c.cache.Clear()
}
10 changes: 5 additions & 5 deletions plumbing/cache/object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
func Test(t *testing.T) { TestingT(t) }

type ObjectSuite struct {
c *ObjectFIFO
c *ObjectLRU
aObject plumbing.EncodedObject
bObject plumbing.EncodedObject
cObject plumbing.EncodedObject
Expand All @@ -27,7 +27,7 @@ func (s *ObjectSuite) SetUpTest(c *C) {
s.cObject = newObject("cccccccccccccccccccccccccccccccccccccccc", 1*Byte)
s.dObject = newObject("dddddddddddddddddddddddddddddddddddddddd", 1*Byte)

s.c = NewObjectFIFO(2 * Byte)
s.c = NewObjectLRU(2 * Byte)
}

func (s *ObjectSuite) TestAdd_SameObject(c *C) {
Expand All @@ -43,16 +43,16 @@ func (s *ObjectSuite) TestAdd_BigObject(c *C) {
c.Assert(s.c.actualSize, Equals, 0*KiByte)
c.Assert(s.c.actualSize, Equals, 0*MiByte)
c.Assert(s.c.actualSize, Equals, 0*GiByte)
c.Assert(len(s.c.objects), Equals, 0)
c.Assert(s.c.cache.Len(), Equals, 0)
}

func (s *ObjectSuite) TestAdd_CacheOverflow(c *C) {
s.c.Add(s.aObject)
c.Assert(s.c.actualSize, Equals, 1*Byte)
s.c.Add(s.cObject)
c.Assert(len(s.c.objects), Equals, 2)
c.Assert(s.c.cache.Len(), Equals, 2)
s.c.Add(s.dObject)
c.Assert(len(s.c.objects), Equals, 2)
c.Assert(s.c.cache.Len(), Equals, 2)

c.Assert(s.c.Get(s.aObject.Hash()), IsNil)
c.Assert(s.c.Get(s.cObject.Hash()), NotNil)
Expand Down
46 changes: 0 additions & 46 deletions plumbing/cache/queue.go

This file was deleted.

2 changes: 1 addition & 1 deletion plumbing/format/packfile/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func NewDecoderForType(s *Scanner, o storer.EncodedObjectStorer,
offsetToType: make(map[int64]plumbing.ObjectType, 0),
decoderType: t,

cache: cache.NewObjectFIFO(cache.MaxSize),
cache: cache.NewObjectLRU(cache.MaxSize),
}, nil
}

Expand Down