From 55d6f4600989b27a243a2050b0ef5c4b9bde0479 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 7 Aug 2026 13:38:47 -0700 Subject: [PATCH] mount: size the directory listing cache with -dirListCacheEntries Defaults to 200k children, a few tens of megabytes, which covers one very wide directory. Zero disables it. Least recently used directories are dropped to stay inside the budget. A subscription gap means the mount cannot know what it missed, so the resubscribe path drops every cached listing along with the cached-ness flags it already resets. --- weed/command/mount.go | 4 ++++ weed/command/mount_common.go | 1 + weed/mount/weedfs.go | 6 ++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/weed/command/mount.go b/weed/command/mount.go index 6a28c2fe8a..844937b58f 100644 --- a/weed/command/mount.go +++ b/weed/command/mount.go @@ -3,6 +3,8 @@ package command import ( "os" "time" + + "github.com/seaweedfs/seaweedfs/weed/mount/meta_cache" ) type MountOptions struct { @@ -20,6 +22,7 @@ type MountOptions struct { concurrentWriters *int concurrentReaders *int cacheMetaTtlSec *int + dirListCacheEntries *int cacheDirForRead *string cacheDirForWrite *string cacheSizeMBForRead *int64 @@ -114,6 +117,7 @@ func init() { mountOptions.cacheDirForWrite = cmdMount.Flag.String("cacheDirWrite", "", "buffer writes mostly for large files") mountOptions.writeBufferSizeMB = cmdMount.Flag.Int64("writeBufferSizeMB", 0, "global cap on the per-mount write buffer (memory + swap) in MB, 0 means unlimited. Bounds /tmp growth when volume uploads stall") mountOptions.cacheMetaTtlSec = cmdMount.Flag.Int("cacheMetaTtlSec", 60, "metadata cache validity seconds") + mountOptions.dirListCacheEntries = cmdMount.Flag.Int("dirListCacheEntries", meta_cache.DefaultListingCacheEntries, "directory children kept decoded in memory, so reopening a folder rereads nothing; 0 disables") mountOptions.dataCenter = cmdMount.Flag.String("dataCenter", "", "prefer to write to the data center") mountOptions.allowOthers = cmdMount.Flag.Bool("allowOthers", true, "allows other users to access the file system") mountOptions.defaultPermissions = cmdMount.Flag.Bool("defaultPermissions", true, "enforce permissions by the operating system") diff --git a/weed/command/mount_common.go b/weed/command/mount_common.go index 0cd7450d52..6830f18ecb 100644 --- a/weed/command/mount_common.go +++ b/weed/command/mount_common.go @@ -197,6 +197,7 @@ func buildSeaweedFileSystem(option *MountOptions, p fileSystemParams) *mount.WFS CacheDirForWrite: p.cacheDirForWrite, WriteBufferSizeMB: *option.writeBufferSizeMB, CacheMetaTTlSec: *option.cacheMetaTtlSec, + DirListCacheEntries: *option.dirListCacheEntries, DataCenter: *option.dataCenter, Quota: int64(*option.collectionQuota) * 1024 * 1024, LogicalDiskUsage: *option.logicalDiskUsage, diff --git a/weed/mount/weedfs.go b/weed/mount/weedfs.go index 80b2acabf0..16141620ad 100644 --- a/weed/mount/weedfs.go +++ b/weed/mount/weedfs.go @@ -53,6 +53,7 @@ type Option struct { CacheDirForWrite string WriteBufferSizeMB int64 CacheMetaTTlSec int + DirListCacheEntries int DataCenter string Umask os.FileMode Quota int64 @@ -303,7 +304,7 @@ func NewSeaweedFileSystem(option *Option) *WFS { wfs.writeBufferAccountant.SetEvictor(wfs.evictOneWritableChunk) } - wfs.metaCache = meta_cache.NewMetaCache(path.Join(option.getUniqueCacheDirForRead(), "meta"), option.UidGidMapper, + wfs.metaCache = meta_cache.NewMetaCacheWithListingCache(path.Join(option.getUniqueCacheDirForRead(), "meta"), option.UidGidMapper, util.FullPath(option.FilerMountRootPath), option.IncludeSystemEntries, func(path util.FullPath) { @@ -314,7 +315,7 @@ func NewSeaweedFileSystem(option *Option) *WFS { if wfs.inodeToPath.RecordDirectoryUpdate(dirPath, time.Now(), wfs.dirHotWindow, wfs.dirHotThreshold) { wfs.markDirectoryReadThrough(dirPath) } - }) + }, option.DirListCacheEntries) wfs.metaCache.SetPinnedChildFn(wfs.isLocalOnlyEntry) grace.OnInterrupt(func() { // grace calls os.Exit(0) after all hooks, so WaitForAsyncFlush @@ -506,6 +507,7 @@ func (wfs *WFS) StartBackgroundTasks() error { // wipe the root's stale children through the apply loop so the delete // cannot strand a concurrent rebuild cached-but-empty. wfs.inodeToPath.InvalidateAllChildrenCache() + wfs.metaCache.InvalidateAllListings() wfs.purgeDirectoryCache(util.FullPath(wfs.option.FilerMountRootPath)) }, follower) go wfs.loopCheckQuota()