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

Commit f69f530

Browse files
authored
Merge pull request #958 from kuba--/fix-cachesize
Fix potential LRU cache size issue.
2 parents 2fb32d2 + edfc16e commit f69f530

File tree

4 files changed

+66
-24
lines changed

4 files changed

+66
-24
lines changed

plumbing/cache/buffer_lru.go

+12-12
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,23 @@ func (c *BufferLRU) Put(key int64, slice []byte) {
4545
c.ll = list.New()
4646
}
4747

48+
bufSize := FileSize(len(slice))
4849
if ee, ok := c.cache[key]; ok {
50+
oldBuf := ee.Value.(buffer)
51+
// in this case bufSize is a delta: new size - old size
52+
bufSize -= FileSize(len(oldBuf.Slice))
4953
c.ll.MoveToFront(ee)
5054
ee.Value = buffer{key, slice}
51-
return
55+
} else {
56+
if bufSize > c.MaxSize {
57+
return
58+
}
59+
ee := c.ll.PushFront(buffer{key, slice})
60+
c.cache[key] = ee
5261
}
5362

54-
objSize := FileSize(len(slice))
55-
56-
if objSize > c.MaxSize {
57-
return
58-
}
59-
60-
for c.actualSize+objSize > c.MaxSize {
63+
c.actualSize += bufSize
64+
for c.actualSize > c.MaxSize {
6165
last := c.ll.Back()
6266
lastObj := last.Value.(buffer)
6367
lastSize := FileSize(len(lastObj.Slice))
@@ -66,10 +70,6 @@ func (c *BufferLRU) Put(key int64, slice []byte) {
6670
delete(c.cache, lastObj.Key)
6771
c.actualSize -= lastSize
6872
}
69-
70-
ee := c.ll.PushFront(buffer{key, slice})
71-
c.cache[key] = ee
72-
c.actualSize += objSize
7373
}
7474

7575
// Get returns a buffer by its key. It marks the buffer as used. If the buffer

plumbing/cache/buffer_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cache
22

33
import (
4+
"bytes"
45
"sync"
56

67
. "gopkg.in/check.v1"
@@ -38,6 +39,28 @@ func (s *BufferSuite) TestPutSameBuffer(c *C) {
3839
}
3940
}
4041

42+
func (s *ObjectSuite) TestPutSameBufferWithDifferentSize(c *C) {
43+
aBuffer := []byte("a")
44+
bBuffer := []byte("bbb")
45+
cBuffer := []byte("ccccc")
46+
dBuffer := []byte("ddddddd")
47+
48+
cache := NewBufferLRU(7 * Byte)
49+
cache.Put(1, aBuffer)
50+
cache.Put(1, bBuffer)
51+
cache.Put(1, cBuffer)
52+
cache.Put(1, dBuffer)
53+
54+
c.Assert(cache.MaxSize, Equals, 7*Byte)
55+
c.Assert(cache.actualSize, Equals, 7*Byte)
56+
c.Assert(cache.ll.Len(), Equals, 1)
57+
58+
buf, ok := cache.Get(1)
59+
c.Assert(bytes.Equal(buf, dBuffer), Equals, true)
60+
c.Assert(FileSize(len(buf)), Equals, 7*Byte)
61+
c.Assert(ok, Equals, true)
62+
}
63+
4164
func (s *BufferSuite) TestPutBigBuffer(c *C) {
4265
for _, o := range s.c {
4366
o.Put(1, s.bBuffer)

plumbing/cache/object_lru.go

+12-12
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,24 @@ func (c *ObjectLRU) Put(obj plumbing.EncodedObject) {
4242
c.ll = list.New()
4343
}
4444

45+
objSize := FileSize(obj.Size())
4546
key := obj.Hash()
4647
if ee, ok := c.cache[key]; ok {
48+
oldObj := ee.Value.(plumbing.EncodedObject)
49+
// in this case objSize is a delta: new size - old size
50+
objSize -= FileSize(oldObj.Size())
4751
c.ll.MoveToFront(ee)
4852
ee.Value = obj
49-
return
50-
}
51-
52-
objSize := FileSize(obj.Size())
53-
54-
if objSize > c.MaxSize {
55-
return
53+
} else {
54+
if objSize > c.MaxSize {
55+
return
56+
}
57+
ee := c.ll.PushFront(obj)
58+
c.cache[key] = ee
5659
}
5760

58-
for c.actualSize+objSize > c.MaxSize {
61+
c.actualSize += objSize
62+
for c.actualSize > c.MaxSize {
5963
last := c.ll.Back()
6064
lastObj := last.Value.(plumbing.EncodedObject)
6165
lastSize := FileSize(lastObj.Size())
@@ -64,10 +68,6 @@ func (c *ObjectLRU) Put(obj plumbing.EncodedObject) {
6468
delete(c.cache, lastObj.Hash())
6569
c.actualSize -= lastSize
6670
}
67-
68-
ee := c.ll.PushFront(obj)
69-
c.cache[key] = ee
70-
c.actualSize += objSize
7171
}
7272

7373
// Get returns an object by its hash. It marks the object as used. If the object

plumbing/cache/object_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,25 @@ func (s *ObjectSuite) TestPutSameObject(c *C) {
4545
}
4646
}
4747

48+
func (s *ObjectSuite) TestPutSameObjectWithDifferentSize(c *C) {
49+
const hash = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
50+
51+
cache := NewObjectLRU(7 * Byte)
52+
cache.Put(newObject(hash, 1*Byte))
53+
cache.Put(newObject(hash, 3*Byte))
54+
cache.Put(newObject(hash, 5*Byte))
55+
cache.Put(newObject(hash, 7*Byte))
56+
57+
c.Assert(cache.MaxSize, Equals, 7*Byte)
58+
c.Assert(cache.actualSize, Equals, 7*Byte)
59+
c.Assert(cache.ll.Len(), Equals, 1)
60+
61+
obj, ok := cache.Get(plumbing.NewHash(hash))
62+
c.Assert(obj.Hash(), Equals, plumbing.NewHash(hash))
63+
c.Assert(FileSize(obj.Size()), Equals, 7*Byte)
64+
c.Assert(ok, Equals, true)
65+
}
66+
4867
func (s *ObjectSuite) TestPutBigObject(c *C) {
4968
for _, o := range s.c {
5069
o.Put(s.bObject)

0 commit comments

Comments
 (0)