vfs: Added cache cleaner for directories to reduce memory usage

This empties the directory cache after twice the directory cache
period to release memory.

Signed-off-by: Anagh Kumar Baranwal <6824881+darthShadow@users.noreply.github.com>
This commit is contained in:
Anagh Kumar Baranwal 2022-11-10 17:04:57 +05:30 committed by Nick Craig-Wood
parent 9a66563fc6
commit 52e25c43b9

View File

@ -22,9 +22,10 @@ import (
// Dir represents a directory entry // Dir represents a directory entry
type Dir struct { type Dir struct {
vfs *VFS // read only vfs *VFS // read only
inode uint64 // read only: inode number inode uint64 // read only: inode number
f fs.Fs // read only f fs.Fs // read only
cleanupTimer *time.Timer // read only: timer to call cacheCleanup
mu sync.RWMutex // protects the following mu sync.RWMutex // protects the following
parent *Dir // parent, nil for root parent *Dir // parent, nil for root
@ -52,7 +53,7 @@ const (
) )
func newDir(vfs *VFS, f fs.Fs, parent *Dir, fsDir fs.Directory) *Dir { func newDir(vfs *VFS, f fs.Fs, parent *Dir, fsDir fs.Directory) *Dir {
return &Dir{ d := &Dir{
vfs: vfs, vfs: vfs,
f: f, f: f,
parent: parent, parent: parent,
@ -62,6 +63,25 @@ func newDir(vfs *VFS, f fs.Fs, parent *Dir, fsDir fs.Directory) *Dir {
inode: newInode(), inode: newInode(),
items: make(map[string]Node), items: make(map[string]Node),
} }
d.cleanupTimer = time.AfterFunc(vfs.Opt.DirCacheTime*2, d.cacheCleanup)
return d
}
func (d *Dir) cacheCleanup() {
defer func() {
// We should never panic here
_ = recover()
}()
when := time.Now()
d.mu.Lock()
_, stale := d._age(when)
d.mu.Unlock()
if stale {
d.ForgetAll()
}
} }
// String converts it to printable // String converts it to printable
@ -182,8 +202,8 @@ func (d *Dir) Node() Node {
// so could not be forgotten. Children which didn't have virtual entries and // so could not be forgotten. Children which didn't have virtual entries and
// children with virtual entries will be forgotten even if true is returned. // children with virtual entries will be forgotten even if true is returned.
func (d *Dir) ForgetAll() (hasVirtual bool) { func (d *Dir) ForgetAll() (hasVirtual bool) {
d.mu.Lock() d.mu.RLock()
defer d.mu.Unlock()
fs.Debugf(d.path, "forgetting directory cache") fs.Debugf(d.path, "forgetting directory cache")
for _, node := range d.items { for _, node := range d.items {
if dir, ok := node.(*Dir); ok { if dir, ok := node.(*Dir); ok {
@ -192,19 +212,29 @@ func (d *Dir) ForgetAll() (hasVirtual bool) {
} }
} }
} }
d.mu.RUnlock()
d.mu.Lock()
defer d.mu.Unlock()
// Purge any unnecessary virtual entries // Purge any unnecessary virtual entries
d._purgeVirtual() d._purgeVirtual()
d.read = time.Time{} d.read = time.Time{}
// Check if this dir has virtual entries // Check if this dir has virtual entries
if len(d.virtual) != 0 { if len(d.virtual) != 0 {
hasVirtual = true hasVirtual = true
} }
// Don't clear directory entries if there are virtual entries in this // Don't clear directory entries if there are virtual entries in this
// directory or any children // directory or any children
if !hasVirtual { if !hasVirtual {
d.items = make(map[string]Node) d.items = make(map[string]Node)
d.cleanupTimer.Stop()
} }
return hasVirtual return hasVirtual
} }
@ -475,6 +505,8 @@ func (d *Dir) _readDir() error {
} }
d.read = when d.read = when
d.cleanupTimer.Reset(d.vfs.Opt.DirCacheTime * 2)
return nil return nil
} }
@ -654,6 +686,7 @@ func (d *Dir) _readDirFromEntries(entries fs.DirEntries, dirTree dirtree.DirTree
dir.read = time.Time{} dir.read = time.Time{}
} else { } else {
dir.read = when dir.read = when
dir.cleanupTimer.Reset(d.vfs.Opt.DirCacheTime * 2)
} }
dir.mu.Unlock() dir.mu.Unlock()
if err != nil { if err != nil {
@ -691,6 +724,7 @@ func (d *Dir) readDirTree() error {
} }
fs.Debugf(d.path, "Reading directory tree done in %s", time.Since(when)) fs.Debugf(d.path, "Reading directory tree done in %s", time.Since(when))
d.read = when d.read = when
d.cleanupTimer.Reset(d.vfs.Opt.DirCacheTime * 2)
return nil return nil
} }