mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-25 14:30:43 +01:00
fix(mount): keep a deferred local create from vanishing when its dir is rebuilt (#9991)
wfs.Create defers the filer create to Flush and inserts a local-only placeholder into the metaCache (dirtyMetadata=true), so a just-created file exists locally before the filer has it. When the parent directory falls out of cache (hot-dir read-through, idle evict) and is rebuilt, EnsureVisited wipes the store and refills from a filer listing that does not yet include the un-flushed create, then markCachedFn publishes the directory authoritatively cached without it. lookupEntry then returns an authoritative ENOENT and ReadDir returns nothing — the file disappears from the mount although the client created it. Under concurrent read+write churn on one directory this is the ConcurrentReadWrite flake. Preserve children the mount flags as local-only (open dirty handle or pending async flush — the signal lookupEntry already trusts) across the rebuild's wipe instead of blind-deleting them. Unpinned stale children are still dropped so a rebuild cannot resurrect a deleted entry.
This commit is contained in:
1 parent
5e8152b81c
commit
93f91c96ca
4 files changed
+153
-3
No files matched your search
@@ -3,6 +3,7 @@ package meta_cache
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -32,7 +33,8 @@ type MetaCache struct {
|
||||
isCachedFn func(fullpath util.FullPath) bool
|
||||
invalidateFunc func(fullpath util.FullPath, entry *filer_pb.Entry)
|
||||
onDirectoryUpdate func(dir util.FullPath)
|
||||
visitGroup singleflight.Group // deduplicates concurrent EnsureVisited calls for the same path
|
||||
pinnedChildFn func(util.FullPath) bool // a child a rebuild must not drop (local-only, not yet on the filer); nil disables
|
||||
visitGroup singleflight.Group // deduplicates concurrent EnsureVisited calls for the same path
|
||||
applyCh chan metadataApplyRequest
|
||||
applyDone chan struct{}
|
||||
applyStateMu sync.Mutex
|
||||
@@ -338,6 +340,42 @@ func (mc *MetaCache) DeleteFolderChildren(ctx context.Context, fp util.FullPath)
|
||||
return mc.localStore.DeleteFolderChildren(ctx, fp)
|
||||
}
|
||||
|
||||
// SetPinnedChildFn installs a predicate reporting whether a child holds
|
||||
// local-only state a rebuild must not discard. See deleteFolderChildrenForRebuild.
|
||||
func (mc *MetaCache) SetPinnedChildFn(fn func(util.FullPath) bool) {
|
||||
mc.pinnedChildFn = fn
|
||||
}
|
||||
|
||||
// deleteFolderChildrenForRebuild clears a directory's cached children ahead of a
|
||||
// rebuild, but keeps any child flagged pinned by pinnedChildFn — a local-only
|
||||
// create not yet flushed to the filer. A rebuild refills from a filer listing
|
||||
// that does not include such a create; a blind wipe would drop it and then
|
||||
// markCachedFn publishes the directory authoritatively cached without a file the
|
||||
// client created, so it vanishes from the mount.
|
||||
func (mc *MetaCache) deleteFolderChildrenForRebuild(ctx context.Context, dirPath util.FullPath) error {
|
||||
mc.Lock()
|
||||
defer mc.Unlock()
|
||||
if mc.pinnedChildFn == nil {
|
||||
return mc.localStore.DeleteFolderChildren(ctx, dirPath)
|
||||
}
|
||||
var pinned []*filer.Entry
|
||||
if _, err := mc.localStore.ListDirectoryEntries(ctx, dirPath, "", true, math.MaxInt64, func(entry *filer.Entry) (bool, error) {
|
||||
if mc.pinnedChildFn(entry.FullPath) {
|
||||
pinned = append(pinned, entry)
|
||||
}
|
||||
return true, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mc.localStore.DeleteFolderChildren(ctx, dirPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(pinned) > 0 {
|
||||
return mc.doBatchInsertEntries(ctx, pinned)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *MetaCache) ListDirectoryEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, eachEntryFunc filer.ListEachEntryFunc) error {
|
||||
mc.RLock()
|
||||
defer mc.RUnlock()
|
||||
|
||||
Reference in new issue
Block a user