refactor: moved the GC stuff around and corrected all tests
All checks were successful
PR Check / check-and-test (pull_request) Successful in 30s
All checks were successful
PR Check / check-and-test (pull_request) Successful in 30s
This commit is contained in:
68
vfs/gc/gc.go
68
vfs/gc/gc.go
@@ -4,10 +4,76 @@ package gc
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"s1d3sw1ped/SteamCache2/steamcache/logger"
|
||||
"s1d3sw1ped/SteamCache2/vfs"
|
||||
"s1d3sw1ped/SteamCache2/vfs/cachestate"
|
||||
"s1d3sw1ped/SteamCache2/vfs/disk"
|
||||
"s1d3sw1ped/SteamCache2/vfs/memory"
|
||||
"s1d3sw1ped/SteamCache2/vfs/vfserror"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LRUGC deletes files in LRU order until enough space is reclaimed.
|
||||
func LRUGC(vfss vfs.VFS, size uint) {
|
||||
attempts := 0
|
||||
deletions := 0
|
||||
var reclaimed uint
|
||||
|
||||
for reclaimed < size {
|
||||
if attempts > 10 {
|
||||
logger.Logger.Debug().
|
||||
Int("attempts", attempts).
|
||||
Msg("GC: Too many attempts to reclaim space, giving up")
|
||||
return
|
||||
}
|
||||
attempts++
|
||||
switch fs := vfss.(type) {
|
||||
case *disk.DiskFS:
|
||||
fi := fs.LRU.Back()
|
||||
if fi == nil {
|
||||
break
|
||||
}
|
||||
sz := uint(fi.Size())
|
||||
err := fs.Delete(fi.Name())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
reclaimed += sz
|
||||
deletions++
|
||||
case *memory.MemoryFS:
|
||||
fi := fs.LRU.Back()
|
||||
if fi == nil {
|
||||
break
|
||||
}
|
||||
sz := uint(fi.Size())
|
||||
err := fs.Delete(fi.Name())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
reclaimed += sz
|
||||
deletions++
|
||||
default:
|
||||
// Fallback to old method if not supported
|
||||
stats := vfss.StatAll()
|
||||
if len(stats) == 0 {
|
||||
break
|
||||
}
|
||||
fi := stats[0] // Assume sorted or pick first
|
||||
sz := uint(fi.Size())
|
||||
err := vfss.Delete(fi.Name())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
reclaimed += sz
|
||||
deletions++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func PromotionDecider(fi *vfs.FileInfo, cs cachestate.CacheState) bool {
|
||||
return time.Since(fi.AccessTime()) < time.Second*60 // Put hot files in the fast vfs if equipped
|
||||
}
|
||||
|
||||
// Ensure GCFS implements VFS.
|
||||
var _ vfs.VFS = (*GCFS)(nil)
|
||||
|
||||
@@ -39,7 +105,7 @@ func (g *GCFS) Create(key string, size int64) (io.WriteCloser, error) {
|
||||
w, err := g.VFS.Create(key, size) // try to create the key
|
||||
|
||||
// if it fails due to disk full error, call the GC handler and try again in a loop that will continue until it succeeds or the error is not disk full
|
||||
for err == vfserror.ErrDiskFull && g.gcHanderFunc != nil { // if the error is disk full and there is a GC handler
|
||||
if err == vfserror.ErrDiskFull && g.gcHanderFunc != nil { // if the error is disk full and there is a GC handler
|
||||
g.gcHanderFunc(g.VFS, uint(size*int64(g.multiplier))) // call the GC handler
|
||||
w, err = g.VFS.Create(key, size)
|
||||
}
|
||||
|
||||
@@ -1,96 +1,73 @@
|
||||
// vfs/gc/gc_test.go
|
||||
package gc
|
||||
|
||||
// func TestGCSmallRandom(t *testing.T) {
|
||||
// t.Parallel()
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"s1d3sw1ped/SteamCache2/vfs/memory"
|
||||
"s1d3sw1ped/SteamCache2/vfs/vfserror"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// m := memory.New(1024 * 1024 * 16)
|
||||
// gc := New(m, 10, func(vfs vfs.VFS, size uint) (uint, uint) {
|
||||
// deletions := 0
|
||||
// var reclaimed uint
|
||||
func TestGCOnFull(t *testing.T) {
|
||||
m := memory.New(10)
|
||||
gc := New(m, 2, LRUGC)
|
||||
|
||||
// t.Logf("GC starting to reclaim %d bytes", size)
|
||||
for i := 0; i < 5; i++ {
|
||||
w, err := gc.Create(fmt.Sprintf("key%d", i), 2)
|
||||
if err != nil {
|
||||
t.Fatalf("Create failed: %v", err)
|
||||
}
|
||||
w.Write([]byte("ab"))
|
||||
w.Close()
|
||||
}
|
||||
|
||||
// stats := vfs.StatAll()
|
||||
// sort.Slice(stats, func(i, j int) bool {
|
||||
// // Sort by access time so we can remove the oldest files first.
|
||||
// return stats[i].AccessTime().Before(stats[j].AccessTime())
|
||||
// })
|
||||
// Cache full at 10 bytes
|
||||
w, err := gc.Create("key5", 2)
|
||||
if err != nil {
|
||||
t.Fatalf("Create failed: %v", err)
|
||||
}
|
||||
w.Write([]byte("cd"))
|
||||
w.Close()
|
||||
|
||||
// // Delete the oldest files until we've reclaimed enough space.
|
||||
// for _, s := range stats {
|
||||
// sz := uint(s.Size()) // Get the size of the file
|
||||
// err := vfs.Delete(s.Name())
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// reclaimed += sz // Track how much space we've reclaimed
|
||||
// deletions++ // Track how many files we've deleted
|
||||
if gc.Size() > 10 {
|
||||
t.Errorf("Size exceeded: %d > 10", gc.Size())
|
||||
}
|
||||
|
||||
// // t.Logf("GC deleting %s, %v", s.Name(), s.AccessTime().Format(time.RFC3339Nano))
|
||||
// Check if older keys were evicted
|
||||
_, err = m.Open("key0")
|
||||
if err == nil {
|
||||
t.Error("Expected key0 to be evicted")
|
||||
}
|
||||
}
|
||||
|
||||
// if reclaimed >= size { // We've reclaimed enough space
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// return uint(reclaimed), uint(deletions)
|
||||
// })
|
||||
func TestNoGCNeeded(t *testing.T) {
|
||||
m := memory.New(20)
|
||||
gc := New(m, 2, LRUGC)
|
||||
|
||||
// for i := 0; i < 10000; i++ {
|
||||
// if err := gc.Set(fmt.Sprintf("key:%d", i), genRandomData(1024*1, 1024*4)); err != nil {
|
||||
// t.Errorf("Set failed: %v", err)
|
||||
// }
|
||||
// }
|
||||
for i := 0; i < 5; i++ {
|
||||
w, err := gc.Create(fmt.Sprintf("key%d", i), 2)
|
||||
if err != nil {
|
||||
t.Fatalf("Create failed: %v", err)
|
||||
}
|
||||
w.Write([]byte("ab"))
|
||||
w.Close()
|
||||
}
|
||||
|
||||
// if gc.Size() > 1024*1024*16 {
|
||||
// t.Errorf("MemoryFS size is %d, want <= 1024", m.Size())
|
||||
// }
|
||||
// }
|
||||
if gc.Size() != 10 {
|
||||
t.Errorf("Size: got %d, want 10", gc.Size())
|
||||
}
|
||||
}
|
||||
|
||||
// func genRandomData(min int, max int) []byte {
|
||||
// data := make([]byte, rand.Intn(max-min)+min)
|
||||
// rand.Read(data)
|
||||
// return data
|
||||
// }
|
||||
func TestGCInsufficientSpace(t *testing.T) {
|
||||
m := memory.New(5)
|
||||
gc := New(m, 1, LRUGC)
|
||||
|
||||
// func TestGCLargeRandom(t *testing.T) {
|
||||
// t.Parallel()
|
||||
|
||||
// m := memory.New(1024 * 1024 * 16) // 16MB
|
||||
// gc := New(m, 10, func(vfs vfs.VFS, size uint) (uint, uint) {
|
||||
// deletions := 0
|
||||
// var reclaimed uint
|
||||
|
||||
// t.Logf("GC starting to reclaim %d bytes", size)
|
||||
|
||||
// stats := vfs.StatAll()
|
||||
// sort.Slice(stats, func(i, j int) bool {
|
||||
// // Sort by access time so we can remove the oldest files first.
|
||||
// return stats[i].AccessTime().Before(stats[j].AccessTime())
|
||||
// })
|
||||
|
||||
// // Delete the oldest files until we've reclaimed enough space.
|
||||
// for _, s := range stats {
|
||||
// sz := uint(s.Size()) // Get the size of the file
|
||||
// vfs.Delete(s.Name())
|
||||
// reclaimed += sz // Track how much space we've reclaimed
|
||||
// deletions++ // Track how many files we've deleted
|
||||
|
||||
// if reclaimed >= size { // We've reclaimed enough space
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
// return uint(reclaimed), uint(deletions)
|
||||
// })
|
||||
|
||||
// for i := 0; i < 10000; i++ {
|
||||
// if err := gc.Set(fmt.Sprintf("key:%d", i), genRandomData(1024, 1024*1024)); err != nil {
|
||||
// t.Errorf("Set failed: %v", err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// if gc.Size() > 1024*1024*16 {
|
||||
// t.Errorf("MemoryFS size is %d, want <= 1024", m.Size())
|
||||
// }
|
||||
// }
|
||||
w, err := gc.Create("key0", 10)
|
||||
if err == nil {
|
||||
w.Close()
|
||||
t.Error("Expected ErrDiskFull")
|
||||
} else if !errors.Is(err, vfserror.ErrDiskFull) {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user