mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-29 00:10:42 +01:00
fix(mount): don't strand a directory cached-but-empty when an eviction races a rebuild (#9791)
* fix(mount): don't strand a directory cached-but-empty when an off-loop wipe races a rebuild Idle eviction, kernel Forget, and the copy-range fallback cleared a directory's cached entries directly, off the metaCache apply loop, after resetting the cached flag in inodeToPath as a separate step. A concurrent rebuild could publish a fresh listing (markCachedFn) in between, so the late DeleteFolderChildren left the directory flagged cached over an empty store. lookupEntry then returns an authoritative ENOENT and ReadDir returns nothing, so every file in the directory disappears from the mount although it is still present on the filer. Route those wipes through a new apply-loop step that resets the flag and wipes the store together, serialized with a build's markCachedFn, and skips a directory while it is building. * fix(mount): route the meta-event retry cleanup through the apply-loop purge The subscription-retry callback wiped the mount root's cached children directly off the apply loop and reset the cache flags as a separate step — the same pattern that can leave a concurrently-rebuilding root cached-but-empty. Invalidate all flags (safe on its own, it never deletes entries) then purge the root's children through the apply loop.
This commit is contained in:
1 parent
3ce4e0dbdf
commit
b5a952bcb1
5 files changed
+157
-10
No files matched your search
@@ -72,6 +72,7 @@ const (
|
||||
metadataBeginBuild
|
||||
metadataCompleteBuild
|
||||
metadataAbortBuild
|
||||
metadataPurgeDir
|
||||
metadataShutdown
|
||||
)
|
||||
|
||||
@@ -82,6 +83,7 @@ type metadataApplyRequest struct {
|
||||
options MetadataResponseApplyOptions
|
||||
buildPath util.FullPath
|
||||
snapshotTsNs int64
|
||||
resetFn func()
|
||||
done chan error
|
||||
}
|
||||
|
||||
@@ -272,6 +274,20 @@ func (mc *MetaCache) AbortDirectoryBuild(ctx context.Context, dirPath util.FullP
|
||||
})
|
||||
}
|
||||
|
||||
// PurgeDirectoryChildren asynchronously clears a directory's cached children and
|
||||
// resets its cached flag (resetFn) via the apply loop. Asynchronous so callers
|
||||
// like kernel Forget don't block; see purgeDirectoryChildrenNow for why off-loop
|
||||
// callers must route through here rather than wiping the store directly.
|
||||
func (mc *MetaCache) PurgeDirectoryChildren(dirPath util.FullPath, resetFn func()) {
|
||||
_ = mc.enqueueApplyRequest(metadataApplyRequest{
|
||||
ctx: context.Background(),
|
||||
kind: metadataPurgeDir,
|
||||
buildPath: dirPath,
|
||||
resetFn: resetFn,
|
||||
done: make(chan error, 1),
|
||||
})
|
||||
}
|
||||
|
||||
func (mc *MetaCache) UpdateEntry(ctx context.Context, entry *filer.Entry) error {
|
||||
mc.Lock()
|
||||
defer mc.Unlock()
|
||||
@@ -465,6 +481,8 @@ func (mc *MetaCache) handleApplyRequest(req metadataApplyRequest) error {
|
||||
return mc.completeDirectoryBuildNow(req.ctx, req.buildPath, req.snapshotTsNs)
|
||||
case metadataAbortBuild:
|
||||
return mc.abortDirectoryBuildNow(req.buildPath)
|
||||
case metadataPurgeDir:
|
||||
return mc.purgeDirectoryChildrenNow(req.ctx, req.buildPath, req.resetFn)
|
||||
case metadataShutdown:
|
||||
return nil
|
||||
default:
|
||||
@@ -621,6 +639,24 @@ func (mc *MetaCache) abortDirectoryBuildNow(dirPath util.FullPath) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// purgeDirectoryChildrenNow runs in the apply loop, serialized with
|
||||
// completeDirectoryBuildNow's markCachedFn, so no build publish interleaves
|
||||
// between resetFn (clears the cached flag) and the store wipe. Skipping a
|
||||
// building directory avoids deleting entries the build inserted but hasn't yet
|
||||
// published. Together these keep a directory from ending up flagged cached over
|
||||
// an empty store — which hides every file in it though they remain on the filer.
|
||||
func (mc *MetaCache) purgeDirectoryChildrenNow(ctx context.Context, dirPath util.FullPath, resetFn func()) error {
|
||||
if mc.isBuildingDir(dirPath) {
|
||||
return nil
|
||||
}
|
||||
if resetFn != nil {
|
||||
resetFn()
|
||||
}
|
||||
mc.Lock()
|
||||
defer mc.Unlock()
|
||||
return mc.localStore.DeleteFolderChildren(ctx, dirPath)
|
||||
}
|
||||
|
||||
func (mc *MetaCache) completeDirectoryBuildNow(ctx context.Context, dirPath util.FullPath, snapshotTsNs int64) error {
|
||||
state := mc.buildingDirs[dirPath]
|
||||
delete(mc.buildingDirs, dirPath)
|
||||
|
||||
Reference in new issue
Block a user