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

Commit 5072513

Browse files
authored
Merge pull request #698 from jfontan/improvement/use-decoder-cache
plumbing: cache, enforce the use of cache in packfile decoder
2 parents ca59809 + e39559f commit 5072513

File tree

6 files changed

+113
-68
lines changed

6 files changed

+113
-68
lines changed

plumbing/cache/common.go

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const (
1111

1212
type FileSize int64
1313

14+
const DefaultMaxSize FileSize = 96 * MiByte
15+
1416
// Object is an interface to a object cache.
1517
type Object interface {
1618
// Put puts the given object into the cache. Whether this object will

plumbing/cache/object_lru.go

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ func NewObjectLRU(maxSize FileSize) *ObjectLRU {
2424
return &ObjectLRU{MaxSize: maxSize}
2525
}
2626

27+
// NewObjectLRUDefault creates a new ObjectLRU with the default cache size.
28+
func NewObjectLRUDefault() *ObjectLRU {
29+
return &ObjectLRU{MaxSize: DefaultMaxSize}
30+
}
31+
2732
// Put puts an object into the cache. If the object is already in the cache, it
2833
// will be marked as used. Otherwise, it will be inserted. A single object might
2934
// be evicted to make room for the new object.

plumbing/cache/object_test.go

+60-41
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
func Test(t *testing.T) { TestingT(t) }
1515

1616
type ObjectSuite struct {
17-
c Object
17+
c map[string]Object
1818
aObject plumbing.EncodedObject
1919
bObject plumbing.EncodedObject
2020
cObject plumbing.EncodedObject
@@ -29,70 +29,89 @@ func (s *ObjectSuite) SetUpTest(c *C) {
2929
s.cObject = newObject("cccccccccccccccccccccccccccccccccccccccc", 1*Byte)
3030
s.dObject = newObject("dddddddddddddddddddddddddddddddddddddddd", 1*Byte)
3131

32-
s.c = NewObjectLRU(2 * Byte)
32+
s.c = make(map[string]Object)
33+
s.c["two_bytes"] = NewObjectLRU(2 * Byte)
34+
s.c["default_lru"] = NewObjectLRUDefault()
3335
}
3436

3537
func (s *ObjectSuite) TestPutSameObject(c *C) {
36-
s.c.Put(s.aObject)
37-
s.c.Put(s.aObject)
38-
_, ok := s.c.Get(s.aObject.Hash())
39-
c.Assert(ok, Equals, true)
38+
for _, o := range s.c {
39+
o.Put(s.aObject)
40+
o.Put(s.aObject)
41+
_, ok := o.Get(s.aObject.Hash())
42+
c.Assert(ok, Equals, true)
43+
}
4044
}
4145

4246
func (s *ObjectSuite) TestPutBigObject(c *C) {
43-
s.c.Put(s.bObject)
44-
_, ok := s.c.Get(s.aObject.Hash())
45-
c.Assert(ok, Equals, false)
47+
for _, o := range s.c {
48+
o.Put(s.bObject)
49+
_, ok := o.Get(s.aObject.Hash())
50+
c.Assert(ok, Equals, false)
51+
}
4652
}
4753

4854
func (s *ObjectSuite) TestPutCacheOverflow(c *C) {
49-
s.c.Put(s.aObject)
50-
s.c.Put(s.cObject)
51-
s.c.Put(s.dObject)
55+
// this test only works with an specific size
56+
o := s.c["two_bytes"]
57+
58+
o.Put(s.aObject)
59+
o.Put(s.cObject)
60+
o.Put(s.dObject)
5261

53-
obj, ok := s.c.Get(s.aObject.Hash())
62+
obj, ok := o.Get(s.aObject.Hash())
5463
c.Assert(ok, Equals, false)
5564
c.Assert(obj, IsNil)
56-
obj, ok = s.c.Get(s.cObject.Hash())
65+
obj, ok = o.Get(s.cObject.Hash())
5766
c.Assert(ok, Equals, true)
5867
c.Assert(obj, NotNil)
59-
obj, ok = s.c.Get(s.dObject.Hash())
68+
obj, ok = o.Get(s.dObject.Hash())
6069
c.Assert(ok, Equals, true)
6170
c.Assert(obj, NotNil)
6271
}
6372

6473
func (s *ObjectSuite) TestClear(c *C) {
65-
s.c.Put(s.aObject)
66-
s.c.Clear()
67-
obj, ok := s.c.Get(s.aObject.Hash())
68-
c.Assert(ok, Equals, false)
69-
c.Assert(obj, IsNil)
74+
for _, o := range s.c {
75+
o.Put(s.aObject)
76+
o.Clear()
77+
obj, ok := o.Get(s.aObject.Hash())
78+
c.Assert(ok, Equals, false)
79+
c.Assert(obj, IsNil)
80+
}
7081
}
7182

7283
func (s *ObjectSuite) TestConcurrentAccess(c *C) {
73-
var wg sync.WaitGroup
74-
75-
for i := 0; i < 1000; i++ {
76-
wg.Add(3)
77-
go func(i int) {
78-
s.c.Put(newObject(fmt.Sprint(i), FileSize(i)))
79-
wg.Done()
80-
}(i)
81-
82-
go func(i int) {
83-
if i%30 == 0 {
84-
s.c.Clear()
85-
}
86-
wg.Done()
87-
}(i)
88-
89-
go func(i int) {
90-
s.c.Get(plumbing.NewHash(fmt.Sprint(i)))
91-
wg.Done()
92-
}(i)
84+
for _, o := range s.c {
85+
var wg sync.WaitGroup
86+
87+
for i := 0; i < 1000; i++ {
88+
wg.Add(3)
89+
go func(i int) {
90+
o.Put(newObject(fmt.Sprint(i), FileSize(i)))
91+
wg.Done()
92+
}(i)
93+
94+
go func(i int) {
95+
if i%30 == 0 {
96+
o.Clear()
97+
}
98+
wg.Done()
99+
}(i)
100+
101+
go func(i int) {
102+
o.Get(plumbing.NewHash(fmt.Sprint(i)))
103+
wg.Done()
104+
}(i)
105+
}
106+
107+
wg.Wait()
93108
}
109+
}
110+
111+
func (s *ObjectSuite) TestDefaultLRU(c *C) {
112+
defaultLRU := s.c["default_lru"].(*ObjectLRU)
94113

95-
wg.Wait()
114+
c.Assert(defaultLRU.MaxSize, Equals, DefaultMaxSize)
96115
}
97116

98117
type dummyObject struct {

plumbing/format/packfile/decoder.go

+23-10
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ var (
5252
// is destroyed. The Offsets and CRCs are calculated whether an
5353
// ObjectStorer was provided or not.
5454
type Decoder struct {
55-
DeltaBaseCache cache.Object
55+
deltaBaseCache cache.Object
5656

5757
s *Scanner
5858
o storer.EncodedObjectStorer
@@ -80,15 +80,27 @@ type Decoder struct {
8080
// If the ObjectStorer implements storer.Transactioner, a transaction is created
8181
// during the Decode execution. If anything fails, Rollback is called
8282
func NewDecoder(s *Scanner, o storer.EncodedObjectStorer) (*Decoder, error) {
83-
return NewDecoderForType(s, o, plumbing.AnyObject)
83+
return NewDecoderForType(s, o, plumbing.AnyObject,
84+
cache.NewObjectLRUDefault())
85+
}
86+
87+
// NewDecoderWithCache is a version of NewDecoder where cache can be specified.
88+
func NewDecoderWithCache(s *Scanner, o storer.EncodedObjectStorer,
89+
cacheObject cache.Object) (*Decoder, error) {
90+
91+
return NewDecoderForType(s, o, plumbing.AnyObject, cacheObject)
8492
}
8593

8694
// NewDecoderForType returns a new Decoder but in this case for a specific object type.
8795
// When an object is read using this Decoder instance and it is not of the same type of
8896
// the specified one, nil will be returned. This is intended to avoid the content
89-
// deserialization of all the objects
97+
// deserialization of all the objects.
98+
//
99+
// cacheObject is a cache.Object implementation that is used to speed up the
100+
// process. If cache is not needed you can pass nil. To create an LRU cache
101+
// object with the default size you can use the helper cache.ObjectLRUDefault().
90102
func NewDecoderForType(s *Scanner, o storer.EncodedObjectStorer,
91-
t plumbing.ObjectType) (*Decoder, error) {
103+
t plumbing.ObjectType, cacheObject cache.Object) (*Decoder, error) {
92104

93105
if t == plumbing.OFSDeltaObject ||
94106
t == plumbing.REFDeltaObject ||
@@ -101,8 +113,9 @@ func NewDecoderForType(s *Scanner, o storer.EncodedObjectStorer,
101113
}
102114

103115
return &Decoder{
104-
s: s,
105-
o: o,
116+
s: s,
117+
o: o,
118+
deltaBaseCache: cacheObject,
106119

107120
idx: NewIndex(0),
108121
offsetToType: make(map[int64]plumbing.ObjectType),
@@ -404,19 +417,19 @@ func (d *Decoder) fillOFSDeltaObjectContent(obj plumbing.EncodedObject, offset i
404417
}
405418

406419
func (d *Decoder) cacheGet(h plumbing.Hash) (plumbing.EncodedObject, bool) {
407-
if d.DeltaBaseCache == nil {
420+
if d.deltaBaseCache == nil {
408421
return nil, false
409422
}
410423

411-
return d.DeltaBaseCache.Get(h)
424+
return d.deltaBaseCache.Get(h)
412425
}
413426

414427
func (d *Decoder) cachePut(obj plumbing.EncodedObject) {
415-
if d.DeltaBaseCache == nil {
428+
if d.deltaBaseCache == nil {
416429
return
417430
}
418431

419-
d.DeltaBaseCache.Put(obj)
432+
d.deltaBaseCache.Put(obj)
420433
}
421434

422435
func (d *Decoder) recallByOffset(o int64) (plumbing.EncodedObject, error) {

plumbing/format/packfile/decoder_test.go

+16-7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"io"
55

66
"gopkg.in/src-d/go-git.v4/plumbing"
7+
"gopkg.in/src-d/go-git.v4/plumbing/cache"
78
"gopkg.in/src-d/go-git.v4/plumbing/format/idxfile"
89
"gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
910
"gopkg.in/src-d/go-git.v4/plumbing/storer"
@@ -51,7 +52,8 @@ func (s *ReaderSuite) TestDecodeByTypeRefDelta(c *C) {
5152

5253
storage := memory.NewStorage()
5354
scanner := packfile.NewScanner(f.Packfile())
54-
d, err := packfile.NewDecoderForType(scanner, storage, plumbing.CommitObject)
55+
d, err := packfile.NewDecoderForType(scanner, storage, plumbing.CommitObject,
56+
cache.NewObjectLRUDefault())
5557
c.Assert(err, IsNil)
5658

5759
// Index required to decode by ref-delta.
@@ -77,7 +79,8 @@ func (s *ReaderSuite) TestDecodeByTypeRefDeltaError(c *C) {
7779
fixtures.Basic().ByTag("ref-delta").Test(c, func(f *fixtures.Fixture) {
7880
storage := memory.NewStorage()
7981
scanner := packfile.NewScanner(f.Packfile())
80-
d, err := packfile.NewDecoderForType(scanner, storage, plumbing.CommitObject)
82+
d, err := packfile.NewDecoderForType(scanner, storage,
83+
plumbing.CommitObject, cache.NewObjectLRUDefault())
8184
c.Assert(err, IsNil)
8285

8386
defer d.Close()
@@ -111,7 +114,8 @@ func (s *ReaderSuite) TestDecodeByType(c *C) {
111114
for _, t := range ts {
112115
storage := memory.NewStorage()
113116
scanner := packfile.NewScanner(f.Packfile())
114-
d, err := packfile.NewDecoderForType(scanner, storage, t)
117+
d, err := packfile.NewDecoderForType(scanner, storage, t,
118+
cache.NewObjectLRUDefault())
115119
c.Assert(err, IsNil)
116120

117121
// when the packfile is ref-delta based, the offsets are required
@@ -141,13 +145,17 @@ func (s *ReaderSuite) TestDecodeByTypeConstructor(c *C) {
141145
storage := memory.NewStorage()
142146
scanner := packfile.NewScanner(f.Packfile())
143147

144-
_, err := packfile.NewDecoderForType(scanner, storage, plumbing.OFSDeltaObject)
148+
_, err := packfile.NewDecoderForType(scanner, storage,
149+
plumbing.OFSDeltaObject, cache.NewObjectLRUDefault())
145150
c.Assert(err, Equals, plumbing.ErrInvalidType)
146151

147-
_, err = packfile.NewDecoderForType(scanner, storage, plumbing.REFDeltaObject)
152+
_, err = packfile.NewDecoderForType(scanner, storage,
153+
plumbing.REFDeltaObject, cache.NewObjectLRUDefault())
154+
148155
c.Assert(err, Equals, plumbing.ErrInvalidType)
149156

150-
_, err = packfile.NewDecoderForType(scanner, storage, plumbing.InvalidObject)
157+
_, err = packfile.NewDecoderForType(scanner, storage, plumbing.InvalidObject,
158+
cache.NewObjectLRUDefault())
151159
c.Assert(err, Equals, plumbing.ErrInvalidType)
152160
}
153161

@@ -313,7 +321,8 @@ func (s *ReaderSuite) TestDecodeObjectAt(c *C) {
313321
func (s *ReaderSuite) TestDecodeObjectAtForType(c *C) {
314322
f := fixtures.Basic().One()
315323
scanner := packfile.NewScanner(f.Packfile())
316-
d, err := packfile.NewDecoderForType(scanner, nil, plumbing.TreeObject)
324+
d, err := packfile.NewDecoderForType(scanner, nil, plumbing.TreeObject,
325+
cache.NewObjectLRUDefault())
317326
c.Assert(err, IsNil)
318327

319328
// when the packfile is ref-delta based, the offsets are required

storage/filesystem/object.go

+7-10
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,17 @@ import (
1818
"gopkg.in/src-d/go-billy.v4"
1919
)
2020

21-
const DefaultMaxDeltaBaseCacheSize = 92 * cache.MiByte
22-
2321
type ObjectStorage struct {
24-
// DeltaBaseCache is an object cache uses to cache delta's bases when
25-
DeltaBaseCache cache.Object
22+
// deltaBaseCache is an object cache uses to cache delta's bases when
23+
deltaBaseCache cache.Object
2624

2725
dir *dotgit.DotGit
2826
index map[plumbing.Hash]*packfile.Index
2927
}
3028

3129
func newObjectStorage(dir *dotgit.DotGit) (ObjectStorage, error) {
3230
s := ObjectStorage{
33-
DeltaBaseCache: cache.NewObjectLRU(DefaultMaxDeltaBaseCacheSize),
31+
deltaBaseCache: cache.NewObjectLRUDefault(),
3432
dir: dir,
3533
}
3634

@@ -287,13 +285,13 @@ func (s *ObjectStorage) decodeObjectAt(
287285

288286
p := packfile.NewScanner(f)
289287

290-
d, err := packfile.NewDecoder(p, memory.NewStorage())
288+
d, err := packfile.NewDecoderWithCache(p, memory.NewStorage(),
289+
s.deltaBaseCache)
291290
if err != nil {
292291
return nil, err
293292
}
294293

295294
d.SetIndex(idx)
296-
d.DeltaBaseCache = s.DeltaBaseCache
297295
obj, err := d.DecodeObjectAt(offset)
298296
return obj, err
299297
}
@@ -400,7 +398,7 @@ func (s *ObjectStorage) buildPackfileIters(t plumbing.ObjectType, seen map[plumb
400398
return nil, err
401399
}
402400

403-
iter, err := newPackfileIter(pack, t, seen, s.index[h], s.DeltaBaseCache)
401+
iter, err := newPackfileIter(pack, t, seen, s.index[h], s.deltaBaseCache)
404402
if err != nil {
405403
return nil, err
406404
}
@@ -433,13 +431,12 @@ func newPackfileIter(f billy.File, t plumbing.ObjectType, seen map[plumbing.Hash
433431
return nil, err
434432
}
435433

436-
d, err := packfile.NewDecoderForType(s, memory.NewStorage(), t)
434+
d, err := packfile.NewDecoderForType(s, memory.NewStorage(), t, cache)
437435
if err != nil {
438436
return nil, err
439437
}
440438

441439
d.SetIndex(index)
442-
d.DeltaBaseCache = cache
443440

444441
return &packfileIter{
445442
f: f,

0 commit comments

Comments
 (0)