mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-29 08:20:42 +01:00
fix(mount): update parent directory mtime/ctime on deferred file create (#9021)
* fix(mount): update parent directory mtime/ctime on deferred file create * style: run go fmt on mount package
This commit is contained in:
1 parent
cd82a9cb4b
commit
2264941a17
7 files changed
+39
-13
No files matched your search
@@ -42,12 +42,6 @@ tests/unlink/03.t
|
||||
tests/rename/23.t
|
||||
tests/rename/24.t
|
||||
|
||||
# ── Parent directory mtime/ctime on deferred file create ───────────────
|
||||
# When file creation is deferred (not flushed to filer immediately),
|
||||
# the parent directory mtime/ctime cannot be updated without invalidating
|
||||
# the just-cached child entry in the metadata cache.
|
||||
tests/open/00.t
|
||||
|
||||
# ── Directory rename permission edge case ──────────────────────────────
|
||||
# Cross-directory rename of a subdirectory with restricted permissions
|
||||
# causes cascading test failures within the test file.
|
||||
|
||||
@@ -278,6 +278,25 @@ func (mc *MetaCache) UpdateEntry(ctx context.Context, entry *filer.Entry) error
|
||||
return mc.localStore.UpdateEntry(ctx, entry)
|
||||
}
|
||||
|
||||
// TouchDirMtimeCtime updates the mtime and ctime of a directory entry
|
||||
// directly in the local metadata cache store. This avoids a filer RPC
|
||||
// round-trip and the associated metadata event that would invalidate
|
||||
// recently cached child entries.
|
||||
func (mc *MetaCache) TouchDirMtimeCtime(ctx context.Context, dirPath util.FullPath, now time.Time) error {
|
||||
mc.Lock()
|
||||
defer mc.Unlock()
|
||||
entry, err := mc.localStore.FindEntry(ctx, dirPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if entry == nil {
|
||||
return nil
|
||||
}
|
||||
entry.Attr.Mtime = now
|
||||
entry.Attr.Ctime = now
|
||||
return mc.localStore.UpdateEntry(ctx, entry)
|
||||
}
|
||||
|
||||
func (mc *MetaCache) FindEntry(ctx context.Context, fp util.FullPath) (entry *filer.Entry, err error) {
|
||||
mc.RLock()
|
||||
defer mc.RUnlock()
|
||||
|
||||
@@ -63,9 +63,9 @@ type Option struct {
|
||||
MountMtime time.Time
|
||||
MountParentInode uint64
|
||||
|
||||
VolumeServerAccess string // how to access volume servers
|
||||
Cipher bool // whether encrypt data on volume server
|
||||
UidGidMapper *meta_cache.UidGidMapper
|
||||
VolumeServerAccess string // how to access volume servers
|
||||
Cipher bool // whether encrypt data on volume server
|
||||
UidGidMapper *meta_cache.UidGidMapper
|
||||
IncludeSystemEntries bool
|
||||
|
||||
// Periodic metadata flush interval in seconds (0 to disable)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mount
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -279,6 +280,17 @@ func (wfs *WFS) touchDirMtimeCtime(dirPath util.FullPath) {
|
||||
wfs.saveEntry(dirPath, dirEntry)
|
||||
}
|
||||
|
||||
// touchDirMtimeCtimeLocal updates a directory's mtime and ctime directly
|
||||
// in the local metadata cache, without a filer RPC. This is used for
|
||||
// deferred file creates where a filer round-trip would invalidate the
|
||||
// just-cached child entry.
|
||||
func (wfs *WFS) touchDirMtimeCtimeLocal(dirPath util.FullPath) {
|
||||
now := time.Now()
|
||||
if err := wfs.metaCache.TouchDirMtimeCtime(context.Background(), dirPath, now); err != nil {
|
||||
glog.V(3).Infof("touchDirMtimeCtimeLocal %s: %v", dirPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
const atimeMapMaxSize = 8192
|
||||
|
||||
// setAtime stores an in-memory atime for an inode. The map is bounded;
|
||||
|
||||
@@ -56,7 +56,6 @@ func (wfs *WFS) Mkdir(cancel <-chan struct{}, in *fuse.MkdirIn, name string, out
|
||||
// but BEFORE outputPbEntry writes attributes to the kernel. We restore
|
||||
// explicitly below instead of using defer so the kernel gets local values.
|
||||
|
||||
|
||||
request := &filer_pb.CreateEntryRequest{
|
||||
Directory: string(dirFullPath),
|
||||
Entry: newEntry,
|
||||
|
||||
@@ -312,6 +312,8 @@ func (wfs *WFS) createRegularFile(dirFullPath util.FullPath, name string, mode u
|
||||
if insertErr := wfs.metaCache.InsertEntry(context.Background(), filer.FromPbEntry(string(dirFullPath), newEntry)); insertErr != nil {
|
||||
glog.Warningf("createFile %s: insert local entry: %v", entryFullPath, insertErr)
|
||||
}
|
||||
wfs.inodeToPath.TouchDirectory(dirFullPath)
|
||||
wfs.touchDirMtimeCtimeLocal(dirFullPath)
|
||||
glog.V(3).Infof("createFile %s: deferred to flush", entryFullPath)
|
||||
return inode, newEntry, fuse.OK
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ type streamMutateMux struct {
|
||||
cancel context.CancelFunc
|
||||
grpcConn *grpc.ClientConn // dedicated connection, closed on stream teardown
|
||||
closed bool
|
||||
disabled bool // permanently disabled if filer doesn't support the RPC
|
||||
disabled bool // permanently disabled if filer doesn't support the RPC
|
||||
stopSend chan struct{} // closed to signal the current sendLoop to exit
|
||||
generation uint64 // incremented each time a new stream is created
|
||||
generation uint64 // incremented each time a new stream is created
|
||||
|
||||
nextID atomic.Uint64
|
||||
|
||||
@@ -64,7 +64,7 @@ type streamMutateMux struct {
|
||||
type streamMutateReq struct {
|
||||
req *filer_pb.StreamMutateEntryRequest
|
||||
errCh chan error // send error feedback
|
||||
gen uint64 // stream generation this request targets
|
||||
gen uint64 // stream generation this request targets
|
||||
}
|
||||
|
||||
func newStreamMutateMux(wfs *WFS) *streamMutateMux {
|
||||
|
||||
Reference in new issue
Block a user