This commit is contained in:
royalcat 2024-03-28 16:09:42 +03:00
parent 7b1863109c
commit ef751771d2
107 changed files with 9435 additions and 850 deletions
src/export/nfs

127
src/export/nfs/kvhandler.go Normal file
View file

@ -0,0 +1,127 @@
package nfs
import (
"context"
"fmt"
"path"
"slices"
"time"
"git.kmsign.ru/royalcat/tstor/pkg/go-nfs"
"git.kmsign.ru/royalcat/tstor/src/config"
"github.com/google/uuid"
"github.com/royalcat/kv"
)
const lifetime = time.Hour * 24
// NewKvHandler provides a basic to/from-file handle cache that can be tuned with a smaller cache of active directory listings.
func NewKvHandler(h nfs.Handler, fs nfs.Filesystem) (nfs.Handler, error) {
activeHandles, err := kv.NewBadgerKVMarhsler[uuid.UUID, []string](path.Join(config.Config.Mounts.NFS.CachePath, "handlers"))
if err != nil {
return nil, err
}
// if s, ok := activeHandles.(kv.BadgerStore); ok {
// db := s.BadgerDB()
// enable with managed database
// go func() {
// for n := range time.NewTimer(lifetime / 2).C {
// db.SetDiscardTs(uint64(n.Add(-lifetime).Unix()))
// }
// }()
// }
return &CachingHandler{
Handler: h,
fs: fs,
activeHandles: activeHandles,
}, nil
}
// CachingHandler implements to/from handle via an LRU cache.
type CachingHandler struct {
nfs.Handler
fs nfs.Filesystem
activeHandles kv.Store[uuid.UUID, []string]
}
// ToHandle takes a file and represents it with an opaque handle to reference it.
// In stateless nfs (when it's serving a unix fs) this can be the device + inode
// but we can generalize with a stateful local cache of handed out IDs.
func (c *CachingHandler) ToHandle(_ nfs.Filesystem, path []string) []byte {
ctx := context.Background()
var id uuid.UUID
c.activeHandles.Range(ctx, func(k uuid.UUID, v []string) bool {
if slices.Equal(path, v) {
id = k
return false
}
return true
})
if id != uuid.Nil {
return id[:]
}
id = uuid.New()
c.activeHandles.Set(ctx, id, path)
return id[:]
}
// FromHandle converts from an opaque handle to the file it represents
func (c *CachingHandler) FromHandle(fh []byte) (nfs.Filesystem, []string, error) {
ctx := context.Background()
id, err := uuid.FromBytes(fh)
if err != nil {
return nil, []string{}, err
}
paths, found, err := c.activeHandles.Get(ctx, id)
if err != nil {
return nil, nil, fmt.Errorf("kv error: %w", err)
}
if found {
return c.fs, paths, nil
}
return nil, []string{}, &nfs.NFSStatusError{NFSStatus: nfs.NFSStatusStale}
}
func (c *CachingHandler) InvalidateHandle(fs nfs.Filesystem, handle []byte) error {
ctx := context.Background()
//Remove from cache
id, err := uuid.FromBytes(handle)
if err != nil {
return err
}
c.activeHandles.Delete(ctx, id)
return nil
}
const maxInt = int(^uint(0) >> 1)
// HandleLimit exports how many file handles can be safely stored by this cache.
func (c *CachingHandler) HandleLimit() int {
return maxInt
}
func hasPrefix(path, prefix []string) bool {
if len(prefix) > len(path) {
return false
}
for i, e := range prefix {
if path[i] != e {
return false
}
}
return true
}