Skip to content

Commit c0bfc58

Browse files
authored
Merge pull request #497 from jfontan/fix/close-siva-files
gitbase: close siva FS after use
2 parents 59b622a + fb03afc commit c0bfc58

23 files changed

+440
-47
lines changed

blobs.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,15 @@ func (i *blobsKeyValueIter) Close() error {
396396
if i.blobs != nil {
397397
i.blobs.Close()
398398
}
399+
400+
if i.idx != nil {
401+
i.idx.Close()
402+
}
403+
404+
if i.repo != nil {
405+
i.repo.Close()
406+
}
407+
399408
return nil
400409
}
401410

blobs_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,11 @@ func TestBlobsIndex(t *testing.T) {
236236
)},
237237
)
238238
}
239+
240+
func TestBlobsIndexIterClosed(t *testing.T) {
241+
testTableIndexIterClosed(t, new(blobsTable))
242+
}
243+
244+
func TestBlobsIterClosed(t *testing.T) {
245+
testTableIterClosed(t, new(blobsTable))
246+
}

commit_blobs_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package gitbase
22

33
import (
4+
"io"
45
"testing"
56

67
"github.com/stretchr/testify/require"
78
"gopkg.in/src-d/go-git.v4/plumbing"
89
"gopkg.in/src-d/go-mysql-server.v0/sql"
910
"gopkg.in/src-d/go-mysql-server.v0/sql/expression"
11+
"gopkg.in/src-d/go-mysql-server.v0/sql/plan"
1012
)
1113

1214
func TestCommitBlobsTableRowIter(t *testing.T) {
@@ -202,3 +204,29 @@ func TestCommitBlobsRowKeyMapper(t *testing.T) {
202204

203205
require.Equal(row, row2)
204206
}
207+
208+
func TestCommitBlobsIndexIterClosed(t *testing.T) {
209+
testTableIndexIterClosed(t, new(commitBlobsTable))
210+
}
211+
212+
// This one is not using testTableIterClosed as it takes too much time
213+
// to go through all the rows. Here we limit it to the first 100.
214+
func TestCommitBlobsIterClosed(t *testing.T) {
215+
require := require.New(t)
216+
ctx, closed := setupSivaCloseRepos(t, "_testdata")
217+
218+
table := new(commitBlobsTable)
219+
iter, err := plan.NewResolvedTable(table).RowIter(ctx)
220+
require.NoError(err)
221+
222+
for i := 0; i < 100; i++ {
223+
_, err = iter.Next()
224+
if err != nil {
225+
require.Equal(io.EOF, err)
226+
break
227+
}
228+
}
229+
230+
iter.Close()
231+
require.True(closed.Check())
232+
}

commit_files.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@ func (i *commitFilesRowIter) Close() error {
301301
i.files.Close()
302302
}
303303

304+
if i.repo != nil {
305+
i.repo.Close()
306+
}
307+
304308
if i.index != nil {
305309
return i.index.Close()
306310
}
@@ -488,6 +492,14 @@ func (i *commitFilesKeyValueIter) Close() error {
488492
i.files.Close()
489493
}
490494

495+
if i.idx != nil {
496+
i.idx.Close()
497+
}
498+
499+
if i.repo != nil {
500+
i.repo.Close()
501+
}
502+
491503
return nil
492504
}
493505

commit_files_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,11 @@ func TestEncodeCommitFileIndexKey(t *testing.T) {
9898

9999
require.Equal(k, k2)
100100
}
101+
102+
func TestCommitFilesIndexIterClosed(t *testing.T) {
103+
testTableIndexIterClosed(t, new(commitFilesTable))
104+
}
105+
106+
func TestCommitFilesIterClosed(t *testing.T) {
107+
testTableIterClosed(t, new(commitFilesTable))
108+
}

commit_trees_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,11 @@ func TestCommitTreesRowKeyMapper(t *testing.T) {
168168

169169
require.Equal(row, row2)
170170
}
171+
172+
func TestCommitTreesIndexIterClosed(t *testing.T) {
173+
testTableIndexIterClosed(t, new(commitTreesTable))
174+
}
175+
176+
func TestCommitTreesIterClosed(t *testing.T) {
177+
testTableIterClosed(t, new(commitTreesTable))
178+
}

commits.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,9 @@ func (i *commitIter) Close() error {
190190
i.iter.Close()
191191
}
192192

193-
i.repo.Close()
193+
if i.repo != nil {
194+
i.repo.Close()
195+
}
194196

195197
return nil
196198
}
@@ -275,6 +277,10 @@ func (i *commitsByHashIter) Close() {
275277
if i.commitIter != nil {
276278
i.commitIter.Close()
277279
}
280+
281+
if i.repo != nil {
282+
i.repo.Close()
283+
}
278284
}
279285

280286
func (i *commitsByHashIter) nextScan() (*object.Commit, error) {
@@ -374,6 +380,15 @@ func (i *commitsKeyValueIter) Close() error {
374380
if i.commits != nil {
375381
i.commits.Close()
376382
}
383+
384+
if i.idx != nil {
385+
i.idx.Close()
386+
}
387+
388+
if i.repo != nil {
389+
i.repo.Close()
390+
}
391+
377392
return nil
378393
}
379394

commits_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,11 @@ func TestCommitsIndex(t *testing.T) {
298298
)},
299299
)
300300
}
301+
302+
func TestCommitsIndexIterClosed(t *testing.T) {
303+
testTableIndexIterClosed(t, new(commitsTable))
304+
}
305+
306+
func TestCommitsIterClosed(t *testing.T) {
307+
testTableIterClosed(t, new(commitsTable))
308+
}

common_test.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,22 @@ package gitbase
22

33
import (
44
"context"
5+
"io"
6+
"io/ioutil"
7+
"os"
8+
"path/filepath"
9+
"strings"
10+
"sync"
511
"testing"
612

713
"github.com/stretchr/testify/require"
14+
"gopkg.in/src-d/go-billy-siva.v4"
15+
billy "gopkg.in/src-d/go-billy.v4"
16+
"gopkg.in/src-d/go-billy.v4/osfs"
817
fixtures "gopkg.in/src-d/go-git-fixtures.v3"
18+
git "gopkg.in/src-d/go-git.v4"
919
"gopkg.in/src-d/go-git.v4/plumbing/cache"
20+
"gopkg.in/src-d/go-git.v4/storage/filesystem"
1021
"gopkg.in/src-d/go-mysql-server.v0/sql"
1122
"gopkg.in/src-d/go-mysql-server.v0/sql/plan"
1223
)
@@ -65,3 +76,180 @@ func buildSession(t *testing.T, repos fixtures.Fixtures,
6576
func tableToRows(ctx *sql.Context, t sql.Table) ([]sql.Row, error) {
6677
return sql.NodeToRows(ctx, plan.NewResolvedTable(t))
6778
}
79+
80+
/*
81+
82+
The following code adds utilities to test that siva files are properly closed.
83+
Instead of using normal setup you can use setupSivaCloseRepos, it returns
84+
a context with a pool with all the sivas in "_testdata" directory. It also
85+
tracks all siva filesystems opened. Its closed state can be checked with
86+
closedSiva.Check().
87+
88+
*/
89+
90+
type closedSiva struct {
91+
closed []bool
92+
m sync.Mutex
93+
}
94+
95+
func (c *closedSiva) NewFS(path string) (billy.Filesystem, error) {
96+
c.m.Lock()
97+
defer c.m.Unlock()
98+
99+
localfs := osfs.New(filepath.Dir(path))
100+
101+
tmpDir, err := ioutil.TempDir(os.TempDir(), "gitbase-siva")
102+
if err != nil {
103+
return nil, err
104+
}
105+
106+
tmpfs := osfs.New(tmpDir)
107+
108+
fs, err := sivafs.NewFilesystem(localfs, filepath.Base(path), tmpfs)
109+
if err != nil {
110+
return nil, err
111+
}
112+
113+
pos := len(c.closed)
114+
c.closed = append(c.closed, false)
115+
116+
fun := func() {
117+
c.m.Lock()
118+
defer c.m.Unlock()
119+
c.closed[pos] = true
120+
}
121+
122+
return &closedSivaFilesystem{fs, fun}, nil
123+
}
124+
125+
func (c *closedSiva) Check() bool {
126+
for _, f := range c.closed {
127+
if !f {
128+
return false
129+
}
130+
}
131+
132+
return true
133+
}
134+
135+
type closedSivaFilesystem struct {
136+
sivafs.SivaFS
137+
closeFunc func()
138+
}
139+
140+
func (c *closedSivaFilesystem) Sync() error {
141+
if c.closeFunc != nil {
142+
c.closeFunc()
143+
}
144+
145+
return c.SivaFS.Sync()
146+
}
147+
148+
var _ repository = new(closedSivaRepository)
149+
150+
type closedSivaRepository struct {
151+
path string
152+
siva *closedSiva
153+
cache cache.Object
154+
}
155+
156+
func (c *closedSivaRepository) ID() string {
157+
return c.path
158+
}
159+
160+
func (c *closedSivaRepository) Repo() (*Repository, error) {
161+
fs, err := c.FS()
162+
if err != nil {
163+
return nil, err
164+
}
165+
166+
s := fs.(*closedSivaFilesystem)
167+
closeFunc := func() { s.Sync() }
168+
169+
sto := filesystem.NewStorageWithOptions(fs, c.Cache(), gitStorerOptions)
170+
repo, err := git.Open(sto, nil)
171+
if err != nil {
172+
return nil, err
173+
174+
}
175+
176+
return NewRepository(c.path, repo, closeFunc), nil
177+
}
178+
179+
func (c *closedSivaRepository) FS() (billy.Filesystem, error) {
180+
return c.siva.NewFS(c.path)
181+
}
182+
183+
func (c *closedSivaRepository) Path() string {
184+
return c.path
185+
}
186+
187+
func (c *closedSivaRepository) Cache() cache.Object {
188+
if c.cache == nil {
189+
c.cache = cache.NewObjectLRUDefault()
190+
}
191+
192+
return c.cache
193+
}
194+
195+
// setupSivaCloseRepos creates a pool with siva files that can be checked
196+
// if they've been closed.
197+
func setupSivaCloseRepos(t *testing.T, dir string) (*sql.Context, *closedSiva) {
198+
require := require.New(t)
199+
200+
t.Helper()
201+
202+
cs := new(closedSiva)
203+
pool := NewRepositoryPool(cache.DefaultMaxSize)
204+
205+
filepath.Walk(dir,
206+
func(path string, info os.FileInfo, err error) error {
207+
if strings.HasSuffix(path, ".siva") {
208+
repo := &closedSivaRepository{path: path, siva: cs}
209+
err := pool.Add(repo)
210+
require.NoError(err)
211+
}
212+
213+
return nil
214+
},
215+
)
216+
217+
session := NewSession(pool, WithSkipGitErrors(true))
218+
ctx := sql.NewContext(context.TODO(), sql.WithSession(session))
219+
220+
return ctx, cs
221+
}
222+
223+
func testTableIndexIterClosed(t *testing.T, table sql.IndexableTable) {
224+
t.Helper()
225+
226+
require := require.New(t)
227+
ctx, closed := setupSivaCloseRepos(t, "_testdata")
228+
229+
iter, err := table.IndexKeyValues(ctx, nil)
230+
require.NoError(err)
231+
232+
for {
233+
_, i, err := iter.Next()
234+
if err != nil {
235+
require.Equal(io.EOF, err)
236+
break
237+
}
238+
239+
i.Close()
240+
}
241+
242+
iter.Close()
243+
require.True(closed.Check())
244+
}
245+
246+
func testTableIterClosed(t *testing.T, table sql.IndexableTable) {
247+
t.Helper()
248+
249+
require := require.New(t)
250+
ctx, closed := setupSivaCloseRepos(t, "_testdata")
251+
_, err := tableToRows(ctx, table)
252+
require.NoError(err)
253+
254+
require.True(closed.Check())
255+
}

files.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,14 @@ func (i *filesKeyValueIter) Close() error {
492492
i.files.Close()
493493
}
494494

495+
if i.idx != nil {
496+
i.idx.Close()
497+
}
498+
499+
if i.repo != nil {
500+
i.repo.Close()
501+
}
502+
495503
return nil
496504
}
497505

0 commit comments

Comments
 (0)