vfs/cache: Fix promoteToFast race with WriteCloser Close
CI / vulncheck (pull_request) Successful in 14s
CI / check-and-test (pull_request) Successful in 40s

promoteToFast read FileInfo.Size from Stat after the VFS lock was
released, while WriteCloser.Close updated Size on the same live
object. Concurrent Create/Open on overlapping keys tripped the race
detector.

Stat now returns a FileInfo snapshot, and promotion uses the
ReadAll length for the fast-tier Create size. Promotion stays
best-effort.

#21
This commit is contained in:
s1d3sw1ped_bot
2026-09-01 20:30:40 +00:00
parent 5d006ac44f
commit 8e09c89e24
6 changed files with 94 additions and 8 deletions
+11
View File
@@ -27,6 +27,17 @@ func NewFileInfo(key string, size int64) *FileInfo {
}
}
// Clone returns a snapshot copy of fi. Stat returns Clone() so callers can
// read Size and other fields without racing Close/Open mutations of the
// in-map FileInfo.
func (fi *FileInfo) Clone() *FileInfo {
if fi == nil {
return nil
}
cp := *fi
return &cp
}
// NewFileInfoFromOS creates a FileInfo from os.FileInfo
func NewFileInfoFromOS(info os.FileInfo, key string) *FileInfo {
return &FileInfo{
+28
View File
@@ -16,6 +16,34 @@ func TestNewFileInfo(t *testing.T) {
}
}
func TestFileInfoClone(t *testing.T) {
t.Parallel()
fi := NewFileInfo("k", 42)
fi.AccessCount = 7
cp := fi.Clone()
if cp == fi {
t.Fatal("Clone returned the same pointer")
}
if cp.Key != fi.Key || cp.Size != fi.Size || cp.AccessCount != fi.AccessCount {
t.Errorf("Clone mismatch: %+v vs %+v", cp, fi)
}
if !cp.ATime.Equal(fi.ATime) || !cp.CTime.Equal(fi.CTime) {
t.Error("Clone timestamps mismatch")
}
cp.Size = 99
cp.AccessCount = 1
if fi.Size != 42 || fi.AccessCount != 7 {
t.Error("mutating Clone affected original")
}
if NewFileInfo("x", 1).Clone() == nil {
t.Error("Clone of non-nil was nil")
}
var none *FileInfo
if none.Clone() != nil {
t.Error("Clone of nil was non-nil")
}
}
func TestUpdateAccess(t *testing.T) {
t.Parallel()
fi := NewFileInfo("k", 1)