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

Commit ae1c4f3

Browse files
committed
plumbing/cache: change FIFO to LRU cache
1 parent b3fc776 commit ae1c4f3

File tree

4 files changed

+94
-91
lines changed

4 files changed

+94
-91
lines changed

plumbing/cache/object.go

Lines changed: 0 additions & 73 deletions
This file was deleted.

plumbing/cache/object_lru.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package cache
2+
3+
import (
4+
"container/list"
5+
6+
"gopkg.in/src-d/go-git.v4/plumbing"
7+
)
8+
9+
// ObjectLRU implements an object cache with an LRU eviction policy and a
10+
// maximum size (measured in object size).
11+
type ObjectLRU struct {
12+
MaxSize FileSize
13+
14+
actualSize FileSize
15+
ll *list.List
16+
cache map[interface{}]*list.Element
17+
}
18+
19+
// NewObjectLRU creates a new ObjectLRU with the given maximum size. The maximum
20+
// size will never be exceeded.
21+
func NewObjectLRU(maxSize FileSize) *ObjectLRU {
22+
return &ObjectLRU{MaxSize: maxSize}
23+
}
24+
25+
// Put puts an object into the cache. If the object is already in the cache, it
26+
// will be marked as used. Otherwise, it will be inserted. A single object might
27+
// be evicted to make room for the new object.
28+
func (c *ObjectLRU) Put(obj plumbing.EncodedObject) {
29+
if c.cache == nil {
30+
c.actualSize = 0
31+
c.cache = make(map[interface{}]*list.Element, 1000)
32+
c.ll = list.New()
33+
}
34+
35+
key := obj.Hash()
36+
if ee, ok := c.cache[key]; ok {
37+
c.ll.MoveToFront(ee)
38+
ee.Value = obj
39+
return
40+
}
41+
42+
objSize := FileSize(obj.Size())
43+
44+
if objSize >= c.MaxSize {
45+
return
46+
}
47+
48+
if c.actualSize+objSize > c.MaxSize {
49+
last := c.ll.Back()
50+
lastObj := last.Value.(plumbing.EncodedObject)
51+
lastSize := FileSize(lastObj.Size())
52+
53+
c.ll.Remove(last)
54+
delete(c.cache, lastObj.Hash())
55+
c.actualSize -= lastSize
56+
57+
if c.actualSize+objSize > c.MaxSize {
58+
return
59+
}
60+
}
61+
62+
ee := c.ll.PushFront(obj)
63+
c.cache[key] = ee
64+
c.actualSize += objSize
65+
}
66+
67+
// Get returns an object by its hash. It marks the object as used. If the object
68+
// is not in the cache, (nil, false) will be returned.
69+
func (c *ObjectLRU) Get(k plumbing.Hash) (plumbing.EncodedObject, bool) {
70+
ee, ok := c.cache[k]
71+
if !ok {
72+
return nil, false
73+
}
74+
75+
c.ll.MoveToFront(ee)
76+
return ee.Value.(plumbing.EncodedObject), true
77+
}
78+
79+
// Clear the content of this object cache.
80+
func (c *ObjectLRU) Clear() {
81+
c.ll = nil
82+
c.cache = nil
83+
c.actualSize = 0
84+
}

plumbing/cache/object_test.go

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
func Test(t *testing.T) { TestingT(t) }
1313

1414
type ObjectSuite struct {
15-
c *ObjectFIFO
15+
c Object
1616
aObject plumbing.EncodedObject
1717
bObject plumbing.EncodedObject
1818
cObject plumbing.EncodedObject
@@ -27,32 +27,26 @@ func (s *ObjectSuite) SetUpTest(c *C) {
2727
s.cObject = newObject("cccccccccccccccccccccccccccccccccccccccc", 1*Byte)
2828
s.dObject = newObject("dddddddddddddddddddddddddddddddddddddddd", 1*Byte)
2929

30-
s.c = NewObjectFIFO(2 * Byte)
30+
s.c = NewObjectLRU(2 * Byte)
3131
}
3232

33-
func (s *ObjectSuite) TestAdd_SameObject(c *C) {
33+
func (s *ObjectSuite) TestPutSameObject(c *C) {
3434
s.c.Put(s.aObject)
35-
c.Assert(s.c.actualSize, Equals, 1*Byte)
3635
s.c.Put(s.aObject)
37-
c.Assert(s.c.actualSize, Equals, 1*Byte)
36+
_, ok := s.c.Get(s.aObject.Hash())
37+
c.Assert(ok, Equals, true)
3838
}
3939

40-
func (s *ObjectSuite) TestAdd_BigObject(c *C) {
40+
func (s *ObjectSuite) TestPutBigObject(c *C) {
4141
s.c.Put(s.bObject)
42-
c.Assert(s.c.actualSize, Equals, 0*Byte)
43-
c.Assert(s.c.actualSize, Equals, 0*KiByte)
44-
c.Assert(s.c.actualSize, Equals, 0*MiByte)
45-
c.Assert(s.c.actualSize, Equals, 0*GiByte)
46-
c.Assert(len(s.c.objects), Equals, 0)
42+
_, ok := s.c.Get(s.aObject.Hash())
43+
c.Assert(ok, Equals, false)
4744
}
4845

49-
func (s *ObjectSuite) TestAdd_CacheOverflow(c *C) {
46+
func (s *ObjectSuite) TestPutCacheOverflow(c *C) {
5047
s.c.Put(s.aObject)
51-
c.Assert(s.c.actualSize, Equals, 1*Byte)
5248
s.c.Put(s.cObject)
53-
c.Assert(len(s.c.objects), Equals, 2)
5449
s.c.Put(s.dObject)
55-
c.Assert(len(s.c.objects), Equals, 2)
5650

5751
obj, ok := s.c.Get(s.aObject.Hash())
5852
c.Assert(ok, Equals, false)
@@ -67,9 +61,7 @@ func (s *ObjectSuite) TestAdd_CacheOverflow(c *C) {
6761

6862
func (s *ObjectSuite) TestClear(c *C) {
6963
s.c.Put(s.aObject)
70-
c.Assert(s.c.actualSize, Equals, 1*Byte)
7164
s.c.Clear()
72-
c.Assert(s.c.actualSize, Equals, 0*Byte)
7365
obj, ok := s.c.Get(s.aObject.Hash())
7466
c.Assert(ok, Equals, false)
7567
c.Assert(obj, IsNil)

storage/filesystem/object.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type ObjectStorage struct {
2929

3030
func newObjectStorage(dir *dotgit.DotGit) (ObjectStorage, error) {
3131
s := ObjectStorage{
32-
DeltaBaseCache: cache.NewObjectFIFO(DefaultMaxDeltaBaseCacheSize),
32+
DeltaBaseCache: cache.NewObjectLRU(DefaultMaxDeltaBaseCacheSize),
3333
dir: dir,
3434
}
3535

0 commit comments

Comments
 (0)