vfs/cache: Fix promoteToFast race with WriteCloser Close
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:
Vendored
+4
-2
@@ -186,7 +186,7 @@ func (tc *TieredCache) Capacity() int64 {
|
||||
func (tc *TieredCache) promoteToFast(key string, reader io.ReadCloser) {
|
||||
defer func() { _ = reader.Close() }() // best-effort close; error secondary to promotion attempt (async best-effort path)
|
||||
|
||||
// Get file info from slow tier to determine size
|
||||
// Size for the space/ReadAll guards comes from a Stat snapshot, not the live in-map FileInfo.
|
||||
var size int64
|
||||
if slow := tc.slow.Load(); slow != nil {
|
||||
if vfs, ok := slow.(vfs.VFS); ok {
|
||||
@@ -210,7 +210,7 @@ func (tc *TieredCache) promoteToFast(key string, reader io.ReadCloser) {
|
||||
}
|
||||
|
||||
// Guard promotion ReadAll using already-fetched size (in addition to space check above)
|
||||
if size > 0 && size > (1<<30) { // conservative 1GB hard limit on promotion reads (aligns with typical max_object_size)
|
||||
if size > (1 << 30) { // conservative 1GB hard limit on promotion reads (aligns with typical max_object_size)
|
||||
return
|
||||
}
|
||||
// Read the entire file content
|
||||
@@ -218,6 +218,8 @@ func (tc *TieredCache) promoteToFast(key string, reader io.ReadCloser) {
|
||||
if err != nil {
|
||||
return // Skip promotion if read fails
|
||||
}
|
||||
// Create with the bytes we actually hold so we never reuse a live FileInfo.Size.
|
||||
size = int64(len(content))
|
||||
|
||||
// Create the file in fast tier
|
||||
if fast := tc.fast.Load(); fast != nil {
|
||||
|
||||
Reference in New Issue
Block a user