Skip to content

Commit a99611f

Browse files
More caching tricks (#12)
1 parent 317e229 commit a99611f

File tree

6 files changed

+107
-21
lines changed

6 files changed

+107
-21
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ require (
4747
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
4848
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
4949
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e // indirect
50+
github.com/djherbis/atime v1.1.0 // indirect
5051
github.com/dustin/go-humanize v1.0.0 // indirect
5152
github.com/fsnotify/fsnotify v1.7.0 // indirect
5253
github.com/fxamacker/cbor/v2 v2.6.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
7373
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
7474
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q=
7575
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A=
76+
github.com/djherbis/atime v1.1.0 h1:rgwVbP/5by8BvvjBNrbh64Qz33idKT3pSnMSJsxhi0g=
77+
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
7678
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
7779
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
7880
github.com/dsnet/try v0.0.3 h1:ptR59SsrcFUYbT/FhAbKTV6iLkeD6O18qfIWRml2fqI=

pkg/blobfs.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type BlobFsMetadata struct {
3838
Padding uint32 `redis:"padding" json:"padding"`
3939
Uid uint32 `redis:"uid" json:"uid"`
4040
Gid uint32 `redis:"gid" json:"gid"`
41+
Gen uint64 `redis:"gen" json:"gen"`
4142
}
4243

4344
type StorageLayer interface {
@@ -97,8 +98,8 @@ func Mount(ctx context.Context, opts BlobFsSystemOpts) (func() error, <-chan err
9798
}
9899

99100
root, _ := blobfs.Root()
100-
attrTimeout := time.Second * 60
101-
entryTimeout := time.Second * 60
101+
attrTimeout := time.Second * 5
102+
entryTimeout := time.Second * 5
102103
fsOptions := &fs.Options{
103104
AttrTimeout: &attrTimeout,
104105
EntryTimeout: &entryTimeout,

pkg/blobfs_node.go

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"path"
7+
"strings"
78
"syscall"
89

910
"github.com/hanwen/go-fuse/v2/fs"
@@ -51,6 +52,9 @@ func (n *FSNode) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOu
5152
out.Mode = node.Attr.Mode
5253
out.Nlink = node.Attr.Nlink
5354
out.Owner = node.Attr.Owner
55+
out.Atimensec = node.Attr.Atimensec
56+
out.Mtimensec = node.Attr.Mtimensec
57+
out.Ctimensec = node.Attr.Ctimensec
5458

5559
return fs.OK
5660
}
@@ -78,21 +82,14 @@ func metaToAttr(metadata *BlobFsMetadata) fuse.Attr {
7882
}
7983
}
8084

81-
func (n *FSNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
82-
n.log("Lookup called with name: %s", name)
83-
84-
// Construct the full of this file path from root
85-
fullPath := path.Join(n.bfsNode.Path, name)
86-
87-
id := GenerateFsID(fullPath)
88-
metadata, err := n.filesystem.Metadata.GetFsNode(ctx, id)
85+
func (n *FSNode) inodeFromFsId(ctx context.Context, fsId string) (*fs.Inode, *fuse.Attr, error) {
86+
metadata, err := n.filesystem.Metadata.GetFsNode(ctx, fsId)
8987
if err != nil {
90-
return nil, syscall.ENOENT
88+
return nil, nil, syscall.ENOENT
9189
}
9290

9391
// Fill out the child node's attributes
9492
attr := metaToAttr(metadata)
95-
out.Attr = attr
9693

9794
// Create a new Inode on lookup
9895
node := n.NewInode(ctx,
@@ -105,9 +102,46 @@ func (n *FSNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*
105102
Attr: attr,
106103
Target: "",
107104
}, attr: attr},
108-
fs.StableAttr{Mode: metadata.Mode, Ino: metadata.Ino},
105+
fs.StableAttr{Mode: metadata.Mode, Ino: metadata.Ino, Gen: metadata.Gen},
109106
)
110107

108+
return node, &attr, nil
109+
}
110+
111+
func (n *FSNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) {
112+
fullPath := path.Join(n.bfsNode.Path, name) // Construct the full of this file path from root
113+
n.log("Lookup called with path: %s", fullPath)
114+
115+
// Force caching of a specific full path if the path contains a special illegal character '%'
116+
// This is a hack to trigger caching from external callers without going through the GRPC service directly
117+
if strings.Contains(fullPath, "%") {
118+
sourcePath := strings.ReplaceAll(fullPath, "%", "/")
119+
120+
if !n.filesystem.Client.HostsAvailable() {
121+
return nil, syscall.ENOENT
122+
}
123+
124+
n.log("Storing content from source with path: %s", sourcePath)
125+
_, err := n.filesystem.Client.StoreContentFromSource(sourcePath, 0)
126+
if err != nil {
127+
return nil, syscall.ENOENT
128+
}
129+
130+
node, attr, err := n.inodeFromFsId(ctx, GenerateFsID(sourcePath))
131+
if err != nil {
132+
return nil, syscall.ENOENT
133+
}
134+
135+
out.Attr = *attr
136+
return node, fs.OK
137+
}
138+
139+
node, attr, err := n.inodeFromFsId(ctx, GenerateFsID(fullPath))
140+
if err != nil {
141+
return nil, syscall.ENOENT
142+
}
143+
144+
out.Attr = *attr
111145
return node, fs.OK
112146
}
113147

@@ -118,6 +152,11 @@ func (n *FSNode) Opendir(ctx context.Context) syscall.Errno {
118152

119153
func (n *FSNode) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
120154
n.log("Open called with flags: %v", flags)
155+
156+
if !n.filesystem.Client.HostsAvailable() {
157+
return nil, 0, syscall.EIO
158+
}
159+
121160
return nil, 0, fs.OK
122161
}
123162

pkg/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ func NewBlobCacheClient(ctx context.Context, cfg BlobCacheConfig) (*BlobCacheCli
108108
Config: cfg,
109109
Metadata: metadata,
110110
Client: bc,
111+
Verbose: cfg.DebugMode,
111112
})
112113
if err != nil {
113114
return nil, err

pkg/metadata.go

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ package blobcache
33
import (
44
"context"
55
"fmt"
6+
"os"
67
"path/filepath"
78
"strings"
89
"time"
910

1011
mapset "github.com/deckarep/golang-set/v2"
12+
"github.com/djherbis/atime"
1113
"github.com/hanwen/go-fuse/v2/fuse"
1214
redis "github.com/redis/go-redis/v9"
1315
)
@@ -151,10 +153,10 @@ func (m *BlobCacheMetadata) StoreContentInBlobFs(ctx context.Context, path strin
151153
return err
152154
}
153155

156+
// Initialize default metadata
154157
now := time.Now()
155158
nowSec := uint64(now.Unix())
156159
nowNsec := uint32(now.Nanosecond())
157-
158160
metadata := &BlobFsMetadata{
159161
PID: previousParentId,
160162
ID: currentNodeId,
@@ -170,18 +172,43 @@ func (m *BlobCacheMetadata) StoreContentInBlobFs(ctx context.Context, path strin
170172
Ctimensec: nowNsec,
171173
}
172174

173-
// Since this is the last file, store as a file, not a dir
174-
if path == currentPath {
175-
metadata.Mode = fuse.S_IFREG | 0755
176-
metadata.Hash = hash
177-
metadata.Size = size
175+
// If currentPath matches the input path, use the actual file info
176+
if currentPath == path {
177+
fileInfo, err := os.Stat(currentPath)
178+
if err != nil {
179+
return err
180+
}
181+
182+
// Update metadata fields with actual file info values
183+
modTime := fileInfo.ModTime()
184+
accessTime := atime.Get(fileInfo)
185+
metadata.Mode = uint32(fileInfo.Mode())
186+
metadata.Atime = uint64(accessTime.Unix())
187+
metadata.Atimensec = uint32(accessTime.Nanosecond())
188+
metadata.Mtime = uint64(modTime.Unix())
189+
metadata.Mtimensec = uint32(modTime.Nanosecond())
190+
191+
// Since we cannot get Ctime in a platform-independent way, set it to ModTime
192+
metadata.Ctime = uint64(modTime.Unix())
193+
metadata.Ctimensec = uint32(modTime.Nanosecond())
194+
195+
metadata.Size = uint64(fileInfo.Size())
196+
if fileInfo.IsDir() {
197+
metadata.Hash = GenerateFsID(currentPath)
198+
metadata.Size = 0
199+
} else {
200+
metadata.Hash = hash
201+
metadata.Size = size
202+
}
178203
}
179204

205+
// Set metadata
180206
err = m.SetFsNode(ctx, currentNodeId, metadata)
181207
if err != nil {
182208
return err
183209
}
184210

211+
// Add the current node as a child of the previous node
185212
err = m.AddFsNodeChild(ctx, previousParentId, currentNodeId)
186213
if err != nil {
187214
return err
@@ -196,7 +223,7 @@ func (m *BlobCacheMetadata) StoreContentInBlobFs(ctx context.Context, path strin
196223
func (m *BlobCacheMetadata) GetFsNode(ctx context.Context, id string) (*BlobFsMetadata, error) {
197224
key := MetadataKeys.MetadataFsNode(id)
198225

199-
res, err := m.rdb.HGetAll(context.TODO(), key).Result()
226+
res, err := m.rdb.HGetAll(ctx, key).Result()
200227
if err != nil && err != redis.Nil {
201228
return nil, err
202229
}
@@ -216,7 +243,22 @@ func (m *BlobCacheMetadata) GetFsNode(ctx context.Context, id string) (*BlobFsMe
216243
func (m *BlobCacheMetadata) SetFsNode(ctx context.Context, id string, metadata *BlobFsMetadata) error {
217244
key := MetadataKeys.MetadataFsNode(id)
218245

219-
err := m.rdb.HSet(ctx, key, ToSlice(metadata)).Err()
246+
// If metadata exists, increment inode generation #
247+
res, err := m.rdb.HGetAll(ctx, key).Result()
248+
if err != nil && err != redis.Nil {
249+
return err
250+
}
251+
252+
if len(res) > 0 {
253+
existingMetadata := &BlobFsMetadata{}
254+
if err = ToStruct(res, existingMetadata); err != nil {
255+
return err
256+
}
257+
258+
metadata.Gen = existingMetadata.Gen + 1
259+
}
260+
261+
err = m.rdb.HSet(ctx, key, ToSlice(metadata)).Err()
220262
if err != nil {
221263
return fmt.Errorf("failed to set blobfs node metadata <%v>: %w", key, err)
222264
}

0 commit comments

Comments
 (0)