Add rclone mount --dir-cache-time to control caching of directory entries - fixes #680

This commit is contained in:
Nick Craig-Wood 2016-10-18 14:44:16 +01:00
parent 368cce93ff
commit a02edb9e69
2 changed files with 31 additions and 7 deletions

View File

@ -30,7 +30,7 @@ type Dir struct {
f fs.Fs f fs.Fs
path string path string
mu sync.RWMutex // protects the following mu sync.RWMutex // protects the following
read bool read time.Time // time directory entry last read
items map[string]*DirEntry items map[string]*DirEntry
} }
@ -66,8 +66,15 @@ func (d *Dir) delObject(leaf string) {
func (d *Dir) readDir() error { func (d *Dir) readDir() error {
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock() defer d.mu.Unlock()
if d.read { when := time.Now()
return nil if d.read.IsZero() {
fs.Debug(d.path, "Reading directory")
} else {
age := when.Sub(d.read)
if age < dirCacheTime {
return nil
}
fs.Debug(d.path, "Re-reading directory (%v old)", age)
} }
objs, dirs, err := fs.NewLister().SetLevel(1).Start(d.f, d.path).GetAll() objs, dirs, err := fs.NewLister().SetLevel(1).Start(d.f, d.path).GetAll()
if err == fs.ErrorDirNotFound { if err == fs.ErrorDirNotFound {
@ -76,6 +83,13 @@ func (d *Dir) readDir() error {
} else if err != nil { } else if err != nil {
return err return err
} }
// NB when we re-read a directory after its cache has expired
// we drop the old files which should lead to correct
// behaviour but may not be very efficient.
// Keep a note of the previous contents of the directory
oldItems := d.items
// Cache the items by name // Cache the items by name
d.items = make(map[string]*DirEntry, len(objs)+len(dirs)) d.items = make(map[string]*DirEntry, len(objs)+len(dirs))
for _, obj := range objs { for _, obj := range objs {
@ -87,12 +101,19 @@ func (d *Dir) readDir() error {
} }
for _, dir := range dirs { for _, dir := range dirs {
name := path.Base(dir.Remote()) name := path.Base(dir.Remote())
// Use old dir value if it exists
if oldItem, ok := oldItems[name]; ok {
if _, ok := oldItem.o.(*fs.Dir); ok {
d.items[name] = oldItem
continue
}
}
d.items[name] = &DirEntry{ d.items[name] = &DirEntry{
o: dir, o: dir,
node: nil, node: nil,
} }
} }
d.read = true d.read = when
return nil return nil
} }

View File

@ -7,6 +7,7 @@ package mount
import ( import (
"log" "log"
"os" "os"
"time"
"bazil.org/fuse" "bazil.org/fuse"
"github.com/ncw/rclone/cmd" "github.com/ncw/rclone/cmd"
@ -18,9 +19,10 @@ import (
// Globals // Globals
var ( var (
noModTime = false noModTime = false
debugFUSE = false debugFUSE = false
noSeek = false noSeek = false
dirCacheTime = 5 * 60 * time.Second
// mount options // mount options
readOnly = false readOnly = false
allowNonEmpty = false allowNonEmpty = false
@ -45,6 +47,7 @@ func init() {
mountCmd.Flags().BoolVarP(&noModTime, "no-modtime", "", noModTime, "Don't read the modification time (can speed things up).") mountCmd.Flags().BoolVarP(&noModTime, "no-modtime", "", noModTime, "Don't read the modification time (can speed things up).")
mountCmd.Flags().BoolVarP(&debugFUSE, "debug-fuse", "", debugFUSE, "Debug the FUSE internals - needs -v.") mountCmd.Flags().BoolVarP(&debugFUSE, "debug-fuse", "", debugFUSE, "Debug the FUSE internals - needs -v.")
mountCmd.Flags().BoolVarP(&noSeek, "no-seek", "", noSeek, "Don't allow seeking in files.") mountCmd.Flags().BoolVarP(&noSeek, "no-seek", "", noSeek, "Don't allow seeking in files.")
mountCmd.Flags().DurationVarP(&dirCacheTime, "dir-cache-time", "", dirCacheTime, "Time to cache directory entries for.")
// mount options // mount options
mountCmd.Flags().BoolVarP(&readOnly, "read-only", "", readOnly, "Mount read-only.") mountCmd.Flags().BoolVarP(&readOnly, "read-only", "", readOnly, "Mount read-only.")
mountCmd.Flags().BoolVarP(&allowNonEmpty, "allow-non-empty", "", allowNonEmpty, "Allow mounting over a non-empty directory.") mountCmd.Flags().BoolVarP(&allowNonEmpty, "allow-non-empty", "", allowNonEmpty, "Allow mounting over a non-empty directory.")