mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-13 15:29:12 +01:00
mount: keep an open file publishing when a peer unlinks one of its names
A delete event from another client marked the inode's open handle deleted even when other names still held the hard-linked file, so every later fsync reported success without saving metadata. The delete event's entry carries the link count the filer decided on, so an alias delete now drops only that name, keeps the handle on a name this mount knows, and has the next flush read the survivor's stored entry before publishing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
70d6639155
commit
ee1de6d533
2 files changed
+32
-1
No files matched your search
@@ -937,6 +937,9 @@ type EntryInvalidation struct {
|
||||
// once the path is empty, so this is the only thing left saying whether a
|
||||
// directory or a file went away.
|
||||
WasDirectory bool
|
||||
// LiveHardLinkId is set on a delete that left other names holding the
|
||||
// hard-linked file, by the count the filer kept its chunks on.
|
||||
LiveHardLinkId []byte
|
||||
}
|
||||
|
||||
type metadataResponseSideEffects struct {
|
||||
@@ -1440,7 +1443,11 @@ func collectEntryInvalidations(resp *filer_pb.SubscribeMetadataResponse) []Entry
|
||||
|
||||
if filer_pb.IsDelete(resp) && message.OldEntry != nil {
|
||||
oldKey := util.NewFullPath(resp.Directory, message.OldEntry.Name)
|
||||
invalidations = append(invalidations, EntryInvalidation{Path: oldKey, TsNs: resp.TsNs, Deleted: true, Signatures: signatures, WasDirectory: message.OldEntry.IsDirectory})
|
||||
invalidation := EntryInvalidation{Path: oldKey, TsNs: resp.TsNs, Deleted: true, Signatures: signatures, WasDirectory: message.OldEntry.IsDirectory}
|
||||
if len(message.OldEntry.HardLinkId) > 0 && message.OldEntry.HardLinkCounter > 1 {
|
||||
invalidation.LiveHardLinkId = message.OldEntry.HardLinkId
|
||||
}
|
||||
invalidations = append(invalidations, invalidation)
|
||||
}
|
||||
|
||||
return invalidations
|
||||
|
||||
@@ -1023,6 +1023,12 @@ func (wfs *WFS) invalidateOpenFileHandle(invalidation meta_cache.EntryInvalidati
|
||||
// so no flush recreates the unlinked name. Either way the entry and
|
||||
// dirty pages stay, so the open fd still reads its buffered writes.
|
||||
if invalidation.Deleted {
|
||||
// The next flush reads the survivor's stored entry before
|
||||
// publishing, so the version stays where it was.
|
||||
if len(invalidation.LiveHardLinkId) > 0 && wfs.followSurvivingLink(fh, inode, filePath) {
|
||||
fh.linkRefresh = invalidation.LiveHardLinkId
|
||||
return
|
||||
}
|
||||
fh.isDeleted = true
|
||||
fh.deleteEpoch++
|
||||
}
|
||||
@@ -1061,6 +1067,24 @@ func (wfs *WFS) invalidateOpenFileHandle(invalidation meta_cache.EntryInvalidati
|
||||
return
|
||||
}
|
||||
|
||||
// followSurvivingLink drops a hard link name another client deleted and
|
||||
// keeps the handle on a name of the inode this mount still knows, reporting
|
||||
// false when it knows none.
|
||||
func (wfs *WFS) followSurvivingLink(fh *FileHandle, inode uint64, removed util.FullPath) bool {
|
||||
survives := false
|
||||
for _, p := range wfs.inodeToPath.GetAllPaths(inode) {
|
||||
survives = survives || p != removed
|
||||
}
|
||||
if !survives {
|
||||
return false
|
||||
}
|
||||
wfs.inodeToPath.RemovePath(removed, nil)
|
||||
if survivor, status := wfs.inodeToPath.GetPath(inode); status == fuse.OK {
|
||||
fh.RememberPath(survivor)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (wfs *WFS) LookupFn() wdclient.LookupFileIdFunctionType {
|
||||
if wfs.option.VolumeServerAccess == "filerProxy" {
|
||||
return func(ctx context.Context, fileId string) (targetUrls []string, err error) {
|
||||
|
||||
Reference in new issue
Block a user