fix(filer): prevent nil pointer dereference in CompactFileChunks when context is cancelled (#11690)

* filer: prevent nil pointer dereference in CompactFileChunks when context is cancelled

Signed-off-by: Tyagiquamar <mohdquamartyagi@gmail.com>

* filer: propagate chunk resolution errors through ViewFromChunks

A failed manifest resolution produced a nil interval list; with the nil
guards this silently became an empty view, so replication sinks would
write empty replicas and report success. Return the error so callers
fail instead, and make ChunkStreamReader report it on first read.

---------

Signed-off-by: Tyagiquamar <mohdquamartyagi@gmail.com>
Co-authored-by: Chris Lu <chrislusf@users.noreply.github.com>
Co-authored-by: Chris Lu <chris.lu@gmail.com>
This commit is contained in:
authored and GitHub committed 2026-10-11 22:17:26 +08:00
1 parent cfbfb60817
commit 72da592b27
2 files changed
+39

No files matched your search

+6
View File
@@ -79,6 +79,9 @@ func CompactFileChunks(ctx context.Context, lookupFileIdFn wdclient.LookupFileId
}
func SeparateGarbageChunks(visibles *IntervalList[*VisibleInterval], chunks []*filer_pb.FileChunk) (compacted []*filer_pb.FileChunk, garbage []*filer_pb.FileChunk) {
if visibles == nil {
return chunks, nil
}
fileIds := make(map[string]bool)
for x := visibles.Front(); x != nil; x = x.Next {
interval := x.Value
@@ -225,6 +228,9 @@ func ViewFromVisibleIntervals(visibles *IntervalList[*VisibleInterval], offset i
}
chunkViews = NewIntervalList[*ChunkView]()
if visibles == nil {
return chunkViews
}
for x := visibles.Front(); x != nil; x = x.Next {
chunk := x.Value
+33
View File
@@ -583,3 +583,36 @@ func TestCompactFileChunks3(t *testing.T) {
t.Fatalf("unexpected compacted: %d", len(compacted))
}
}
func TestSeparateGarbageChunksNilVisibles(t *testing.T) {
chunks := []*filer_pb.FileChunk{
{Offset: 10, Size: 100, FileId: "abc", ModifiedTsNs: 50},
}
compacted, garbage := SeparateGarbageChunks(nil, chunks)
if len(compacted) != 1 || len(garbage) != 0 {
t.Fatalf("expected fallback to original chunks with nil garbage")
}
}
func TestViewFromVisibleIntervalsNilVisibles(t *testing.T) {
views := ViewFromVisibleIntervals(nil, 0, 100)
if views == nil || views.Front() != nil {
t.Fatalf("expected empty interval list")
}
}
func TestViewFromChunksPropagatesError(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
chunks := []*filer_pb.FileChunk{
{Offset: 0, Size: 100, FileId: "abc", ModifiedTsNs: 50, IsChunkManifest: true},
}
views, err := ViewFromChunks(ctx, nil, chunks, 0, math.MaxInt64)
if err == nil {
t.Fatal("expected error on cancelled context")
}
if views != nil {
t.Fatalf("expected nil views on error, got %v", views)
}
}