vfs/disk: Fix EvictDiskVisibilityAndRecreateSafety flake
New() launches background calculateSizeAndPopulateIndex which scans disk and calls insertBatch. Create does not wait on initDone, so a file can be written and indexed, discovered by the scan, then removed from d.info and disk by EvictLRU/EvictBySize. insertBatch then re-inserted the stale discoveredFile without checking the path still existed, so Stat succeeded from the index while os.Stat failed. Re-stat under the lock and skip gone files so evicted keys are not resurrected. Fixes #18.
This commit is contained in:
+15
-5
@@ -248,15 +248,25 @@ func (d *DiskFS) calculateSizeAndPopulateIndex() {
|
||||
|
||||
// insertBatch populates info/LRU under lock for a bounded batch (follows maxEvictBatch pattern for short critical sections).
|
||||
// Size is incremented here only for files actually added (prevents double-count vs. concurrent Create during window).
|
||||
// Fail-closed: re-stat each path under d.mu and skip if the file is gone. Create does not wait on
|
||||
// initDone, so a file the scanner observed can be Evict/Delete'd (info + os.Remove) before this
|
||||
// insert runs. Inserting without a live-file check would resurrect the key in d.info and make
|
||||
// Stat succeed while os.Stat fails.
|
||||
func (d *DiskFS) insertBatch(batch []discoveredFile) {
|
||||
d.mu.Lock()
|
||||
for _, df := range batch {
|
||||
if _, exists := d.info[df.key]; !exists {
|
||||
fi := vfs.NewFileInfoFromOS(df.osInfo, df.key)
|
||||
d.info[df.key] = fi
|
||||
d.LRU.Add(df.key, fi)
|
||||
d.size += df.size
|
||||
if _, exists := d.info[df.key]; exists {
|
||||
continue
|
||||
}
|
||||
path := d.pathForKey(df.key)
|
||||
st, err := os.Stat(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fi := vfs.NewFileInfoFromOS(st, df.key)
|
||||
d.info[df.key] = fi
|
||||
d.LRU.Add(df.key, fi)
|
||||
d.size += st.Size()
|
||||
}
|
||||
d.mu.Unlock()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user