fix (plg_backend_nfs): add a cache layer for nfs

This commit is contained in:
MickaelK
2026-03-18 13:37:01 +11:00
parent b6918194a9
commit 2bd1bef586
+82 -18
View File
@@ -1,11 +1,11 @@
package plg_backend_nfs
import (
"context"
"io"
"os"
"path/filepath"
"strings"
"sync"
. "github.com/mickael-kerjean/filestash/server/common"
@@ -15,11 +15,14 @@ import (
"github.com/vmware/go-nfs-client/nfs/xdr"
)
var NfsCache AppCache
type NfsShare struct {
mount *nfs.Mount
v *nfs.Target
auth rpc.Auth
ctx context.Context
mu *sync.Mutex
wg *sync.WaitGroup
uid uint32
gid uint32
gids []uint32
@@ -28,6 +31,22 @@ type NfsShare struct {
func init() {
Backend.Register("nfs", NfsShare{})
util.DefaultLogger.SetDebug(false)
NfsCache = NewAppCache(2, 1)
NfsCache.OnEvict(func(key string, value interface{}) {
c := value.(*NfsShare)
if c == nil {
Log.Warning("plg_backend_nfs::nfs is nil on close")
return
} else if c.wg == nil {
c.Close()
Log.Warning("plg_backend_nfs::wg is nil on close")
return
}
c.wg.Wait()
Log.Debug("plg_backend_nfs::vacuum")
c.Close()
})
}
func (this NfsShare) Init(params map[string]string, app *App) (IBackend, error) {
@@ -38,9 +57,26 @@ func (this NfsShare) Init(params map[string]string, app *App) (IBackend, error)
params["machine_name"] = "Filestash"
}
params["username"] = params["uid"]
uid, gid, gids := ExtractUserInfo(params["uid"], params["gid"], params["gids"])
Log.Debug("plg_backend_nfs::userInfo user=%s uid=%d gid=%d gids=%+v", params["uid"], uid, gid, gids)
if c := NfsCache.Get(params); c != nil {
d := c.(*NfsShare)
if d == nil {
Log.Warning("plg_backend_nfs::nfs is nil on get")
return nil, ErrInternal
} else if d.wg == nil {
Log.Warning("plg_backend_nfs::wg is nil on get")
return nil, ErrInternal
}
d.wg.Add(1)
go func() {
<-app.Context.Done()
d.wg.Done()
}()
return d, nil
}
uid, gid, gids := ExtractUserInfo(params["uid"], params["gid"], params["gids"])
Log.Debug("plg_backend_nfs::userInfo user=%s uid=%d gid=%d gids=%v", params["uid"], uid, gid, gids)
mount, err := nfs.DialMount(params["hostname"])
if err != nil {
return nil, err
@@ -53,7 +89,15 @@ func (this NfsShare) Init(params map[string]string, app *App) (IBackend, error)
if err != nil {
return nil, err
}
return NfsShare{mount, v, auth, app.Context, uid, gid, toGids(gids)}, nil
s := &NfsShare{mount, v, auth, new(sync.Mutex), new(sync.WaitGroup), uid, gid, toGids(gids)}
s.wg.Add(1)
go func() {
<-app.Context.Done()
s.wg.Done()
}()
NfsCache.Set(params, s)
return s, nil
}
func toGids(gids []GroupLabel) []uint32 {
@@ -123,6 +167,8 @@ func (this NfsShare) LoginForm() Form {
}
func (this NfsShare) Meta(path string) Metadata {
this.mu.Lock()
defer this.mu.Unlock()
f, _, err := this.v.Lookup(strings.TrimSuffix(path, "/"))
if err != nil {
return Metadata{}
@@ -184,7 +230,8 @@ func isIn(id uint32, list []uint32) bool {
}
func (this NfsShare) Ls(path string) ([]os.FileInfo, error) {
defer this.Close()
this.mu.Lock()
defer this.mu.Unlock()
files := make([]os.FileInfo, 0)
dirs, err := this.v.ReadDirPlus(path)
@@ -228,30 +275,46 @@ func (this NfsShare) Ls(path string) ([]os.FileInfo, error) {
}
func (this NfsShare) Stat(path string) (os.FileInfo, error) {
defer this.Close()
return nil, ErrNotImplemented
}
func (this NfsShare) Cat(path string) (io.ReadCloser, error) {
go func() {
<-this.ctx.Done()
this.Close()
}()
rc, err := this.v.OpenFile(path, 0777)
this.mu.Lock()
rc, err := this.v.Open(path)
this.mu.Unlock()
if err != nil {
return nil, err
}
return rc, nil
return &nfsReadCloser{rc, this.mu}, nil
}
type nfsReadCloser struct {
io.ReadCloser
mu *sync.Mutex
}
func (r *nfsReadCloser) Read(p []byte) (int, error) {
r.mu.Lock()
defer r.mu.Unlock()
return r.ReadCloser.Read(p)
}
func (r *nfsReadCloser) Close() error {
r.mu.Lock()
defer r.mu.Unlock()
return r.ReadCloser.Close()
}
func (this NfsShare) Mkdir(path string) error {
defer this.Close()
this.mu.Lock()
defer this.mu.Unlock()
_, err := this.v.Mkdir(this.nfsPath(path), 0775)
return err
}
func (this NfsShare) Rm(path string) error {
defer this.Close()
this.mu.Lock()
defer this.mu.Unlock()
if strings.HasSuffix(path, "/") {
return this.v.RemoveAll(this.nfsPath(path))
}
@@ -262,8 +325,8 @@ func (this NfsShare) Rm(path string) error {
// PR aren't handled by vmware, we did come with the implementation as
// of RFC1813 in: https://www.rfc-editor.org/rfc/rfc1813#section-3.3.14
func (this NfsShare) Mv(from string, to string) error {
defer this.Close()
this.mu.Lock()
defer this.mu.Unlock()
f, fName := filepath.Split(this.nfsPath(from))
_, fh, err := this.v.Lookup(f)
if err != nil {
@@ -314,7 +377,8 @@ func (this NfsShare) Touch(path string) error {
}
func (this NfsShare) Save(path string, file io.Reader) error {
defer this.Close()
this.mu.Lock()
defer this.mu.Unlock()
w, err := this.v.OpenFile(path, 0644)
if err != nil {
return err