mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-14 07:48:41 +01:00
mount: retry a read view whose merged manifests did not resolve
A failed SetChunks leaves the chunk group answering every read with the resolution error, and nothing re-ran it while the entry stayed the same. The handle now marks such a view (on rebase and on SetEntry) and the next read or lseek resolves the entry's chunks again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1f2ed6923f
commit
f2e446cf5b
4 files changed
+58
-1
No files matched your search
@@ -4,12 +4,14 @@ package mount
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"slices"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/seaweedfs/go-fuse/v2/fuse"
|
||||
"github.com/seaweedfs/seaweedfs/weed/filer"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/util"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -418,3 +420,37 @@ func TestRebaseEntryKeepsMergedSize(t *testing.T) {
|
||||
t.Fatalf("size %d after a write grew the handle, want 5", size)
|
||||
}
|
||||
}
|
||||
|
||||
// A merged entry whose manifest cannot be resolved yet leaves the read view
|
||||
// marked, and the next read resolves it again instead of failing for good.
|
||||
func TestRebaseEntryRetriesUnresolvedChunkView(t *testing.T) {
|
||||
wfs := newInvalidateTestWFS(t)
|
||||
inode := wfs.inodeToPath.Lookup(util.FullPath("/dir/file"), time.Now().Unix(), false, false, 0, false)
|
||||
fh, _, _, _ := wfs.fhMap.AcquireFileHandle(wfs, inode, flushConditionEntry(1), 0, 0)
|
||||
lookupDown := true
|
||||
group, err := filer.NewChunkGroup(func(ctx context.Context, fileId string) ([]string, error) {
|
||||
if lookupDown {
|
||||
return nil, errors.New("volume lookup down")
|
||||
}
|
||||
return nil, nil
|
||||
}, nil, nil, 1, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fh.entryChunkGroup = group
|
||||
|
||||
sent := proto.Clone(fh.GetEntry().GetEntry()).(*filer_pb.Entry)
|
||||
merged := flushConditionEntry(1, 2)
|
||||
merged.Chunks[1].IsChunkManifest = true
|
||||
fh.rebaseEntry(merged, sent)
|
||||
if !fh.chunkViewStale.Load() {
|
||||
t.Fatal("unresolved manifest not marked")
|
||||
}
|
||||
|
||||
lookupDown = false
|
||||
fh.UpdateEntry(func(e *filer_pb.Entry) { e.Chunks = e.Chunks[:1] })
|
||||
fh.refreshChunkView()
|
||||
if fh.chunkViewStale.Load() {
|
||||
t.Fatal("read view still marked after the chunks resolved")
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,10 @@ type FileHandle struct {
|
||||
// writes can grow the file past it; a merge still clips peer chunks there.
|
||||
truncatedTo atomic.Pointer[truncateMark]
|
||||
|
||||
// chunkViewStale marks a read view whose manifest resolution failed; the
|
||||
// next read or lseek resolves the entry's chunks again.
|
||||
chunkViewStale atomic.Bool
|
||||
|
||||
// dlmLock holds the distributed lock for cross-mount write coordination.
|
||||
// Non-nil only when -dlm is enabled and the file was opened for writing.
|
||||
// Acquired in AcquireHandle, released in ReleaseHandle.
|
||||
@@ -145,6 +149,7 @@ func (fh *FileHandle) SetEntry(entry *filer_pb.Entry) {
|
||||
if resolveManifestErr != nil {
|
||||
glog.Warningf("failed to resolve manifest chunks in %+v", filer_pb.ScrubCipherKeys(entry))
|
||||
}
|
||||
fh.chunkViewStale.Store(resolveManifestErr != nil)
|
||||
} else {
|
||||
glog.Fatalf("setting file handle entry to nil")
|
||||
}
|
||||
@@ -199,7 +204,21 @@ func (fh *FileHandle) rebaseEntry(merged, sent *filer_pb.Entry) {
|
||||
attrs.FileSize = size
|
||||
}
|
||||
e.Attributes, e.Extended, e.Content, e.Chunks = attrs, merged.Extended, merged.Content, chunks
|
||||
fh.entryChunkGroup.SetChunks(chunks)
|
||||
err := fh.entryChunkGroup.SetChunks(chunks)
|
||||
if err != nil {
|
||||
glog.Warningf("rebase %s: resolve chunks: %v", fh.FullPath(), err)
|
||||
}
|
||||
fh.chunkViewStale.Store(err != nil)
|
||||
})
|
||||
}
|
||||
|
||||
// refreshChunkView retries a read view whose manifest resolution failed.
|
||||
func (fh *FileHandle) refreshChunkView() {
|
||||
if !fh.chunkViewStale.Load() {
|
||||
return
|
||||
}
|
||||
fh.UpdateEntry(func(e *filer_pb.Entry) {
|
||||
fh.chunkViewStale.Store(fh.entryChunkGroup.SetChunks(e.Chunks) != nil)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ func (fh *FileHandle) readFromChunksWithContext(ctx context.Context, buff []byte
|
||||
}
|
||||
|
||||
// Fall back to normal chunk reading
|
||||
fh.refreshChunkView()
|
||||
totalRead, ts, err := fh.entryChunkGroup.ReadDataAt(ctx, fileSize, buff, offset)
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
|
||||
@@ -70,6 +70,7 @@ func (wfs *WFS) Lseek(cancel <-chan struct{}, in *fuse.LseekIn, out *fuse.LseekO
|
||||
}()
|
||||
|
||||
// search chunks for the offset
|
||||
fh.refreshChunkView()
|
||||
found, offset, err := fh.entryChunkGroup.SearchChunks(ctx, offset, fileSize, in.Whence)
|
||||
if err != nil {
|
||||
glog.Errorf("Lseek %s: %v", fh.FullPath(), err)
|
||||
|
||||
Reference in new issue
Block a user