Skip to content

Commit f5a305d

Browse files
authored
Merge pull request #1 from juicedata/meta_cache
Add entry cache to speed up lookup
2 parents d43b927 + c9457cb commit f5a305d

File tree

5 files changed

+110
-21
lines changed

5 files changed

+110
-21
lines changed

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/vmware/go-nfs-client
2+
3+
go 1.15
4+
5+
require github.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93 h1:UVArwN/wkKjMVhh2EQGC0tEc1+FqiLlvYXY5mQ2f8Wg=
2+
github.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93/go.mod h1:Nfe4efndBz4TibWycNE+lqyJZiMX4ycx+QKV8Ta0f/o=

nfs/example/test/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"log"
1313
"os"
1414
"strings"
15+
"time"
1516

1617
"github.com/vmware/go-nfs-client/nfs"
1718
"github.com/vmware/go-nfs-client/nfs/rpc"
@@ -33,7 +34,7 @@ func main() {
3334

3435
util.Infof("host=%s target=%s dir=%s\n", host, target, dir)
3536

36-
mount, err := nfs.DialMount(host)
37+
mount, err := nfs.DialMount(host, time.Second)
3738
if err != nil {
3839
log.Fatalf("unable to dial MOUNT service: %v", err)
3940
}

nfs/mount.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package nfs
66
import (
77
"errors"
88
"fmt"
9+
"time"
910

1011
"github.com/vmware/go-nfs-client/nfs/rpc"
1112
"github.com/vmware/go-nfs-client/nfs/xdr"
@@ -34,9 +35,10 @@ const (
3435

3536
type Mount struct {
3637
*rpc.Client
37-
auth rpc.Auth
38-
dirPath string
39-
Addr string
38+
auth rpc.Auth
39+
dirPath string
40+
Addr string
41+
entryTimeout time.Duration
4042
}
4143

4244
func (m *Mount) Unmount() error {
@@ -104,7 +106,7 @@ func (m *Mount) Mount(dirpath string, auth rpc.Auth) (*Target, error) {
104106
m.dirPath = dirpath
105107
m.auth = auth
106108

107-
vol, err := NewTarget(m.Addr, auth, fh, dirpath)
109+
vol, err := NewTarget(m.Addr, auth, fh, dirpath, m.entryTimeout)
108110
if err != nil {
109111
return nil, err
110112
}
@@ -127,7 +129,7 @@ func (m *Mount) Mount(dirpath string, auth rpc.Auth) (*Target, error) {
127129
return nil, fmt.Errorf("unknown mount stat: %d", mountstat3)
128130
}
129131

130-
func DialMount(addr string) (*Mount, error) {
132+
func DialMount(addr string, entryTimeout time.Duration) (*Mount, error) {
131133
// get MOUNT port
132134
m := rpc.Mapping{
133135
Prog: MountProg,
@@ -142,7 +144,8 @@ func DialMount(addr string) (*Mount, error) {
142144
}
143145

144146
return &Mount{
145-
Client: client,
146-
Addr: addr,
147+
Client: client,
148+
Addr: addr,
149+
entryTimeout: entryTimeout,
147150
}, nil
148151
}

nfs/target.go

Lines changed: 91 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,33 @@ import (
99
"path"
1010
"path/filepath"
1111
"strings"
12+
"sync"
13+
"time"
1214

1315
"github.com/vmware/go-nfs-client/nfs/rpc"
1416
"github.com/vmware/go-nfs-client/nfs/util"
1517
"github.com/vmware/go-nfs-client/nfs/xdr"
1618
)
1719

20+
type cacheEntry struct {
21+
fh []byte
22+
attr *Fattr
23+
expire time.Time
24+
}
1825
type Target struct {
1926
*rpc.Client
2027

2128
auth rpc.Auth
2229
fh []byte
2330
dirPath string
2431
fsinfo *FSInfo
32+
33+
entryTimeout time.Duration
34+
cacheM sync.Mutex
35+
entries map[string]map[string]*cacheEntry
2536
}
2637

27-
func NewTarget(addr string, auth rpc.Auth, fh []byte, dirpath string) (*Target, error) {
38+
func NewTarget(addr string, auth rpc.Auth, fh []byte, dirpath string, entryTimeout time.Duration) (*Target, error) {
2839
m := rpc.Mapping{
2940
Prog: Nfs3Prog,
3041
Vers: Nfs3Vers,
@@ -38,10 +49,12 @@ func NewTarget(addr string, auth rpc.Auth, fh []byte, dirpath string) (*Target,
3849
}
3950

4051
vol := &Target{
41-
Client: client,
42-
auth: auth,
43-
fh: fh,
44-
dirPath: dirpath,
52+
Client: client,
53+
auth: auth,
54+
fh: fh,
55+
dirPath: dirpath,
56+
entryTimeout: entryTimeout,
57+
entries: make(map[string]map[string]*cacheEntry),
4558
}
4659

4760
fsinfo, err := vol.FSInfo()
@@ -51,7 +64,7 @@ func NewTarget(addr string, auth rpc.Auth, fh []byte, dirpath string) (*Target,
5164

5265
vol.fsinfo = fsinfo
5366
util.Debugf("%s:%s fsinfo=%#v", addr, dirpath, fsinfo)
54-
67+
go vol.cleanupCache()
5568
return vol, nil
5669
}
5770

@@ -105,6 +118,31 @@ func (v *Target) FSInfo() (*FSInfo, error) {
105118
return fsinfo, nil
106119
}
107120

121+
func (v *Target) cleanupCache() {
122+
for {
123+
v.cacheM.Lock()
124+
now := time.Now()
125+
var cnt int
126+
OUTER:
127+
for fh, es := range v.entries {
128+
for n, e := range es {
129+
if now.After(e.expire) {
130+
delete(es, n)
131+
if len(es) == 0 {
132+
delete(v.entries, fh)
133+
}
134+
}
135+
cnt++
136+
if cnt > 1000 {
137+
break OUTER
138+
}
139+
}
140+
}
141+
v.cacheM.Unlock()
142+
time.Sleep(time.Second)
143+
}
144+
}
145+
108146
// Lookup returns attributes and the file handle to a given dirent
109147
func (v *Target) Lookup(p string) (os.FileInfo, []byte, error) {
110148
var (
@@ -117,22 +155,62 @@ func (v *Target) Lookup(p string) (os.FileInfo, []byte, error) {
117155
dirents := strings.Split(path.Clean(p), "/")
118156
for _, dirent := range dirents {
119157
// we're assuming the root is always the root of the mount
120-
if dirent == "." || dirent == "" {
158+
if dirent == "" {
121159
util.Debugf("root -> 0x%x", fh)
122-
continue
160+
dirent = "."
123161
}
124162

125-
fattr, fh, err = v.lookup(fh, dirent)
163+
fattr, fh, err = v.cachedLookup(fh, dirent)
126164
if err != nil {
127165
return nil, nil, err
128166
}
129167

130168
//util.Debugf("%s -> 0x%x", dirent, fh)
169+
// TODO: resolve symlink
131170
}
132171

133172
return fattr, fh, nil
134173
}
135174

175+
func (v *Target) parsefh(fh []byte) string {
176+
return string(fh)
177+
}
178+
179+
func (v *Target) cachedLookup(fh []byte, name string) (*Fattr, []byte, error) {
180+
ino := v.parsefh(fh)
181+
v.cacheM.Lock()
182+
es := v.entries[ino]
183+
if es != nil {
184+
e := es[name]
185+
if e != nil && time.Since(e.expire) < 0 {
186+
v.cacheM.Unlock()
187+
return e.attr, e.fh, nil
188+
}
189+
}
190+
v.cacheM.Unlock()
191+
attr, fh, err := v.lookup(fh, name)
192+
if err == nil && attr.Type == 2 { // only cache directories
193+
if es == nil {
194+
es = make(map[string]*cacheEntry)
195+
v.entries[ino] = es
196+
}
197+
v.cacheM.Lock()
198+
es[name] = &cacheEntry{fh, attr, time.Now().Add(v.entryTimeout)}
199+
v.cacheM.Unlock()
200+
}
201+
return attr, fh, err
202+
}
203+
204+
func (v *Target) invalidateEntryCache(fh []byte, name string) {
205+
ino := v.parsefh(fh)
206+
v.cacheM.Lock()
207+
es, ok := v.entries[ino]
208+
if ok {
209+
delete(es, name)
210+
}
211+
v.cacheM.Unlock()
212+
}
213+
136214
// lookup returns the same as above, but by fh and name
137215
func (v *Target) lookup(fh []byte, name string) (*Fattr, []byte, error) {
138216
type Lookup3Args struct {
@@ -327,7 +405,7 @@ func (v *Target) Mkdir(path string, perm os.FileMode) ([]byte, error) {
327405
util.Debugf("mkdir(%s) partial response: %+v", mkdirres)
328406
return nil, err
329407
}
330-
408+
v.invalidateEntryCache(fh, newDir)
331409
util.Debugf("mkdir(%s): created successfully (0x%x)", path, fh)
332410
return mkdirres.FH.FH, nil
333411
}
@@ -391,7 +469,7 @@ func (v *Target) Create(path string, perm os.FileMode) ([]byte, error) {
391469
if err = xdr.Read(res, status); err != nil {
392470
return nil, err
393471
}
394-
472+
v.invalidateEntryCache(fh, newFile)
395473
util.Debugf("create(%s): created successfully", path)
396474
return status.FH.FH, nil
397475
}
@@ -433,7 +511,7 @@ func (v *Target) remove(fh []byte, deleteFile string) error {
433511
util.Debugf("remove(%s): %s", deleteFile, err.Error())
434512
return err
435513
}
436-
514+
v.invalidateEntryCache(fh, deleteFile)
437515
return nil
438516
}
439517

@@ -474,7 +552,7 @@ func (v *Target) rmDir(fh []byte, name string) error {
474552
util.Debugf("rmdir(%s): %s", name, err.Error())
475553
return err
476554
}
477-
555+
v.invalidateEntryCache(fh, name)
478556
util.Debugf("rmdir(%s): deleted successfully", name)
479557
return nil
480558
}

0 commit comments

Comments
 (0)