Skip to content

Commit 1316f48

Browse files
committed
Merge startup cancellation and parallel OCI pulls
2 parents 0ae1b5b + b1fd635 commit 1316f48

10 files changed

Lines changed: 2157 additions & 121 deletions

File tree

pkg/clip/clip.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ type MountOptions struct {
6666
CachePath string
6767
ContentCache storage.ContentCache
6868
ContentCacheAvailable bool
69+
StorageModeOverride common.StorageMode
6970
StorageInfo common.ClipStorageInfo
7071
Credentials storage.ClipStorageCredentials
7172
UseCheckpoints bool // Enable checkpoint-based partial decompression for OCI layers
@@ -229,12 +230,15 @@ func MountArchive(options MountOptions) (func() error, <-chan error, *fuse.Serve
229230
go server.Serve()
230231

231232
if err := server.WaitMount(); err != nil {
233+
_ = clipfs.closeViewFiles()
234+
_ = archiveStorage.Cleanup()
232235
serverError <- err
233236
return
234237
}
235238

236239
server.Wait()
237-
archiveStorage.Cleanup()
240+
_ = clipfs.closeViewFiles()
241+
_ = archiveStorage.Cleanup()
238242

239243
close(serverError)
240244
}()
@@ -266,6 +270,7 @@ func openArchiveStorage(options MountOptions) (storage.ClipStorageInterface, err
266270
ArchivePath: options.ArchivePath,
267271
CachePath: options.CachePath,
268272
Metadata: metadata,
273+
StorageModeOverride: options.StorageModeOverride,
269274
Credentials: options.Credentials,
270275
StorageInfo: s3Info,
271276
ContentCache: options.ContentCache,

pkg/clip/clipfs.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package clip
22

33
import (
44
"fmt"
5+
"os"
56
"sync"
7+
"syscall"
68

79
"github.com/beam-cloud/clip/pkg/common"
810
"github.com/beam-cloud/clip/pkg/storage"
@@ -30,6 +32,8 @@ type ClipFileSystem struct {
3032
cachingStatus map[string]bool
3133
cacheEventChan chan cacheEvent
3234
cachingStatusMu sync.Mutex
35+
viewFilesMu sync.Mutex
36+
viewFiles map[string]*os.File
3337
}
3438

3539
type lookupCacheEntry struct {
@@ -49,6 +53,7 @@ func NewFileSystem(s storage.ClipStorageInterface, opts ClipFileSystemOpts) (*Cl
4953
contentCacheReadAhead: storage.NewContentCacheReadAhead(opts.ContentCache, storage.ContentCacheReadAheadOptions{}),
5054
cacheEventChan: make(chan cacheEvent, 10000),
5155
cachingStatus: make(map[string]bool),
56+
viewFiles: make(map[string]*os.File),
5257
contentCacheAvailable: opts.ContentCacheAvailable,
5358
readTraceObserver: opts.ReadTraceObserver,
5459
}
@@ -70,6 +75,39 @@ func NewFileSystem(s storage.ClipStorageInterface, opts ClipFileSystemOpts) (*Cl
7075
return cfs, nil
7176
}
7277

78+
func (cfs *ClipFileSystem) openViewFile(path string) (*os.File, error) {
79+
cfs.viewFilesMu.Lock()
80+
defer cfs.viewFilesMu.Unlock()
81+
82+
if file := cfs.viewFiles[path]; file != nil {
83+
return file, nil
84+
}
85+
if len(cfs.viewFiles) >= clipFileViewFDCacheSize {
86+
return nil, syscall.EMFILE
87+
}
88+
89+
file, err := os.Open(path)
90+
if err != nil {
91+
return nil, err
92+
}
93+
cfs.viewFiles[path] = file
94+
return file, nil
95+
}
96+
97+
func (cfs *ClipFileSystem) closeViewFiles() error {
98+
cfs.viewFilesMu.Lock()
99+
defer cfs.viewFilesMu.Unlock()
100+
101+
var firstErr error
102+
for path, file := range cfs.viewFiles {
103+
if err := file.Close(); err != nil && firstErr == nil {
104+
firstErr = err
105+
}
106+
delete(cfs.viewFiles, path)
107+
}
108+
return firstErr
109+
}
110+
73111
func (cfs *ClipFileSystem) Root() (fs.InodeEmbedder, error) {
74112
if cfs.root == nil {
75113
return nil, fmt.Errorf("root not initialized")

pkg/clip/fsnode.go

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package clip
22

33
import (
44
"context"
5-
"os"
65
"path"
76
"strconv"
87
"sync"
@@ -25,19 +24,14 @@ type FSNode struct {
2524
dirEntries []fuse.DirEntry
2625
}
2726

28-
const clipFileHandleFDCacheSize = 2048
27+
const clipFileViewFDCacheSize = 2048
2928

3029
type clipFileHandle struct {
31-
node *FSNode
32-
mu sync.Mutex
33-
files map[string]*os.File
30+
node *FSNode
3431
}
3532

3633
func newClipFileHandle(node *FSNode) *clipFileHandle {
37-
return &clipFileHandle{
38-
node: node,
39-
files: make(map[string]*os.File),
40-
}
34+
return &clipFileHandle{node: node}
4135
}
4236

4337
func (fh *clipFileHandle) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
@@ -57,33 +51,7 @@ func (fh *clipFileHandle) Read(ctx context.Context, dest []byte, off int64) (fus
5751
}
5852

5953
func (fh *clipFileHandle) Release(ctx context.Context) syscall.Errno {
60-
fh.mu.Lock()
61-
defer fh.mu.Unlock()
62-
var firstErr syscall.Errno
63-
for path, file := range fh.files {
64-
if err := file.Close(); err != nil && firstErr == fs.OK {
65-
firstErr = fs.ToErrno(err)
66-
}
67-
delete(fh.files, path)
68-
}
69-
return firstErr
70-
}
71-
72-
func (fh *clipFileHandle) openViewFile(path string) (*os.File, error) {
73-
fh.mu.Lock()
74-
defer fh.mu.Unlock()
75-
if file := fh.files[path]; file != nil {
76-
return file, nil
77-
}
78-
if len(fh.files) >= clipFileHandleFDCacheSize {
79-
return nil, syscall.EMFILE
80-
}
81-
file, err := os.Open(path)
82-
if err != nil {
83-
return nil, err
84-
}
85-
fh.files[path] = file
86-
return file, nil
54+
return fs.OK
8755
}
8856

8957
func (fh *clipFileHandle) readClientLocalFileView(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, bool, syscall.Errno) {
@@ -119,7 +87,7 @@ func (fh *clipFileHandle) readClientLocalFileView(ctx context.Context, dest []by
11987
return nil, false, fs.OK
12088
}
12189

122-
file, err := fh.openViewFile(view.Path)
90+
file, err := fh.node.filesystem.openViewFile(view.Path)
12391
if err != nil {
12492
if traceRead {
12593
fh.node.observeRead(ctx, fh.node.clientLocalFileViewReadTrace(view, off, readLen, 0, started, err))

pkg/clip/fsnode_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"errors"
99
"fmt"
1010
"os"
11+
"path/filepath"
1112
"strings"
1213
"sync"
1314
"testing"
@@ -272,6 +273,21 @@ func TestLegacyClientLocalFileViewReadWarmsContentCache(t *testing.T) {
272273
require.Equal(t, testData, cache.store[contentHash])
273274
}
274275

276+
func TestClientLocalFileViewsShareBackingFile(t *testing.T) {
277+
archivePath := filepath.Join(t.TempDir(), "layer")
278+
require.NoError(t, os.WriteFile(archivePath, []byte("data"), 0644))
279+
280+
filesystem := &ClipFileSystem{viewFiles: make(map[string]*os.File)}
281+
first, err := filesystem.openViewFile(archivePath)
282+
require.NoError(t, err)
283+
second, err := filesystem.openViewFile(archivePath)
284+
require.NoError(t, err)
285+
require.Same(t, first, second)
286+
require.Len(t, filesystem.viewFiles, 1)
287+
require.NoError(t, filesystem.closeViewFiles())
288+
require.Empty(t, filesystem.viewFiles)
289+
}
290+
275291
func TestImmutableFileOpenSkipsFlush(t *testing.T) {
276292
node := &FSNode{clipNode: &common.ClipNode{NodeType: common.FileNode}}
277293

0 commit comments

Comments
 (0)