|
|
|
|
@@ -75,12 +75,17 @@ func LRUGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Msg("Attempting to reclaim space using LRU GC")
|
|
|
|
|
|
|
|
|
|
var reclaimed uint // reclaimed space in bytes
|
|
|
|
|
deleted := false
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
switch fs := vfss.(type) {
|
|
|
|
|
case *disk.DiskFS:
|
|
|
|
|
fi := fs.LRU.Back()
|
|
|
|
|
if fi == nil {
|
|
|
|
|
if deleted {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using LRU GC (at least one file deleted)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return ErrInsufficientSpace // No files to delete
|
|
|
|
|
}
|
|
|
|
|
sz := uint(fi.Size())
|
|
|
|
|
@@ -89,9 +94,14 @@ func LRUGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
continue // If delete fails, try the next file
|
|
|
|
|
}
|
|
|
|
|
reclaimed += sz
|
|
|
|
|
deleted = true
|
|
|
|
|
case *memory.MemoryFS:
|
|
|
|
|
fi := fs.LRU.Back()
|
|
|
|
|
if fi == nil {
|
|
|
|
|
if deleted {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using LRU GC (at least one file deleted)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return ErrInsufficientSpace // No files to delete
|
|
|
|
|
}
|
|
|
|
|
sz := uint(fi.Size())
|
|
|
|
|
@@ -100,13 +110,14 @@ func LRUGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
continue // If delete fails, try the next file
|
|
|
|
|
}
|
|
|
|
|
reclaimed += sz
|
|
|
|
|
deleted = true
|
|
|
|
|
default:
|
|
|
|
|
panic("unreachable: unsupported VFS type for LRU GC") // panic if the VFS is not disk or memory
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if reclaimed >= size {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using LRU GC")
|
|
|
|
|
return nil // stop if enough space is reclaimed
|
|
|
|
|
if deleted && (size == 0 || reclaimed >= size) {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using LRU GC (at least one file deleted)")
|
|
|
|
|
return nil // stop if enough space is reclaimed or at least one file deleted for size==0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -115,31 +126,32 @@ func LRUGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
func LFUGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Msg("Attempting to reclaim space using LFU GC")
|
|
|
|
|
|
|
|
|
|
// Get all files and sort by access count (frequency)
|
|
|
|
|
files := getAllFiles(vfss)
|
|
|
|
|
if len(files) == 0 {
|
|
|
|
|
return ErrInsufficientSpace
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort by access count (ascending - least frequently used first)
|
|
|
|
|
sort.Slice(files, func(i, j int) bool {
|
|
|
|
|
return files[i].AccessCount < files[j].AccessCount
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
var reclaimed uint
|
|
|
|
|
deleted := false
|
|
|
|
|
for _, fi := range files {
|
|
|
|
|
if reclaimed >= size {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
err := vfss.Delete(fi.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
reclaimed += uint(fi.Size)
|
|
|
|
|
deleted = true
|
|
|
|
|
if deleted && (size == 0 || reclaimed >= size) {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using LFU GC (at least one file deleted)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if reclaimed >= size {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using LFU GC")
|
|
|
|
|
if deleted {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using LFU GC (at least one file deleted)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return ErrInsufficientSpace
|
|
|
|
|
@@ -149,31 +161,32 @@ func LFUGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
func FIFOGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Msg("Attempting to reclaim space using FIFO GC")
|
|
|
|
|
|
|
|
|
|
// Get all files and sort by creation time (oldest first)
|
|
|
|
|
files := getAllFiles(vfss)
|
|
|
|
|
if len(files) == 0 {
|
|
|
|
|
return ErrInsufficientSpace
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort by creation time (ascending - oldest first)
|
|
|
|
|
sort.Slice(files, func(i, j int) bool {
|
|
|
|
|
return files[i].MTime.Before(files[j].MTime)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
var reclaimed uint
|
|
|
|
|
deleted := false
|
|
|
|
|
for _, fi := range files {
|
|
|
|
|
if reclaimed >= size {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
err := vfss.Delete(fi.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
reclaimed += uint(fi.Size)
|
|
|
|
|
deleted = true
|
|
|
|
|
if deleted && (size == 0 || reclaimed >= size) {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using FIFO GC (at least one file deleted)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if reclaimed >= size {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using FIFO GC")
|
|
|
|
|
if deleted {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using FIFO GC (at least one file deleted)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return ErrInsufficientSpace
|
|
|
|
|
@@ -183,31 +196,32 @@ func FIFOGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
func LargestGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Msg("Attempting to reclaim space using Largest GC")
|
|
|
|
|
|
|
|
|
|
// Get all files and sort by size (largest first)
|
|
|
|
|
files := getAllFiles(vfss)
|
|
|
|
|
if len(files) == 0 {
|
|
|
|
|
return ErrInsufficientSpace
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort by size (descending - largest first)
|
|
|
|
|
sort.Slice(files, func(i, j int) bool {
|
|
|
|
|
return files[i].Size > files[j].Size
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
var reclaimed uint
|
|
|
|
|
deleted := false
|
|
|
|
|
for _, fi := range files {
|
|
|
|
|
if reclaimed >= size {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
err := vfss.Delete(fi.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
reclaimed += uint(fi.Size)
|
|
|
|
|
deleted = true
|
|
|
|
|
if deleted && (size == 0 || reclaimed >= size) {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using Largest GC (at least one file deleted)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if reclaimed >= size {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using Largest GC")
|
|
|
|
|
if deleted {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using Largest GC (at least one file deleted)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return ErrInsufficientSpace
|
|
|
|
|
@@ -217,31 +231,32 @@ func LargestGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
func SmallestGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Msg("Attempting to reclaim space using Smallest GC")
|
|
|
|
|
|
|
|
|
|
// Get all files and sort by size (smallest first)
|
|
|
|
|
files := getAllFiles(vfss)
|
|
|
|
|
if len(files) == 0 {
|
|
|
|
|
return ErrInsufficientSpace
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort by size (ascending - smallest first)
|
|
|
|
|
sort.Slice(files, func(i, j int) bool {
|
|
|
|
|
return files[i].Size < files[j].Size
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
var reclaimed uint
|
|
|
|
|
deleted := false
|
|
|
|
|
for _, fi := range files {
|
|
|
|
|
if reclaimed >= size {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
err := vfss.Delete(fi.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
reclaimed += uint(fi.Size)
|
|
|
|
|
deleted = true
|
|
|
|
|
if deleted && (size == 0 || reclaimed >= size) {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using Smallest GC (at least one file deleted)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if reclaimed >= size {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using Smallest GC")
|
|
|
|
|
if deleted {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using Smallest GC (at least one file deleted)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return ErrInsufficientSpace
|
|
|
|
|
@@ -251,14 +266,11 @@ func SmallestGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
func HybridGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Msg("Attempting to reclaim space using Hybrid GC")
|
|
|
|
|
|
|
|
|
|
// Get all files and calculate hybrid scores
|
|
|
|
|
files := getAllFiles(vfss)
|
|
|
|
|
if len(files) == 0 {
|
|
|
|
|
return ErrInsufficientSpace
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate hybrid scores (lower score = more likely to be evicted)
|
|
|
|
|
// Score = (time since last access in seconds) * (file size in MB)
|
|
|
|
|
now := time.Now()
|
|
|
|
|
for i := range files {
|
|
|
|
|
timeSinceAccess := now.Sub(files[i].ATime).Seconds()
|
|
|
|
|
@@ -266,25 +278,27 @@ func HybridGC(vfss vfs.VFS, size uint) error {
|
|
|
|
|
files[i].HybridScore = timeSinceAccess * sizeMB
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort by hybrid score (ascending - lowest scores first)
|
|
|
|
|
sort.Slice(files, func(i, j int) bool {
|
|
|
|
|
return files[i].HybridScore < files[j].HybridScore
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
var reclaimed uint
|
|
|
|
|
deleted := false
|
|
|
|
|
for _, fi := range files {
|
|
|
|
|
if reclaimed >= size {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
err := vfss.Delete(fi.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
reclaimed += uint(fi.Size)
|
|
|
|
|
deleted = true
|
|
|
|
|
if deleted && (size == 0 || reclaimed >= size) {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using Hybrid GC (at least one file deleted)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if reclaimed >= size {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using Hybrid GC")
|
|
|
|
|
if deleted {
|
|
|
|
|
logger.Logger.Debug().Uint("target", size).Uint("achieved", reclaimed).Msg("Reclaimed enough space using Hybrid GC (at least one file deleted)")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return ErrInsufficientSpace
|
|
|
|
|
@@ -308,25 +322,23 @@ func getAllFiles(vfss vfs.VFS) []fileInfoWithMetadata {
|
|
|
|
|
case *disk.DiskFS:
|
|
|
|
|
allFiles := fs.StatAll()
|
|
|
|
|
for _, fi := range allFiles {
|
|
|
|
|
// For disk, we can't easily track access count, so we'll use 1 as default
|
|
|
|
|
files = append(files, fileInfoWithMetadata{
|
|
|
|
|
Name: fi.Name(),
|
|
|
|
|
Size: fi.Size(),
|
|
|
|
|
MTime: fi.ModTime(),
|
|
|
|
|
ATime: fi.AccessTime(),
|
|
|
|
|
AccessCount: 1,
|
|
|
|
|
AccessCount: fi.AccessCount,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
case *memory.MemoryFS:
|
|
|
|
|
allFiles := fs.StatAll()
|
|
|
|
|
for _, fi := range allFiles {
|
|
|
|
|
// For memory, we can't easily track access count, so we'll use 1 as default
|
|
|
|
|
files = append(files, fileInfoWithMetadata{
|
|
|
|
|
Name: fi.Name(),
|
|
|
|
|
Size: fi.Size(),
|
|
|
|
|
MTime: fi.ModTime(),
|
|
|
|
|
ATime: fi.AccessTime(),
|
|
|
|
|
AccessCount: 1,
|
|
|
|
|
AccessCount: fi.AccessCount,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -705,13 +717,17 @@ func New(vfs vfs.VFS, gcHandlerFunc GCHandlerFunc) *GCFS {
|
|
|
|
|
|
|
|
|
|
// Create overrides the Create method of the VFS interface. It tries to create the key, if it fails due to disk full error, it calls the GC handler and tries again. If it still fails it returns the error.
|
|
|
|
|
func (g *GCFS) Create(key string, size int64) (io.WriteCloser, error) {
|
|
|
|
|
w, err := g.VFS.Create(key, size) // try to create the key
|
|
|
|
|
for err == vfserror.ErrDiskFull && g.gcHanderFunc != nil { // if the error is disk full and there is a GC handler
|
|
|
|
|
errr := g.gcHanderFunc(g.VFS, uint(size)) // call the GC handler
|
|
|
|
|
if errr == ErrInsufficientSpace {
|
|
|
|
|
return nil, errr // if the GC handler returns no files to delete, return the error
|
|
|
|
|
w, err := g.VFS.Create(key, size) // try to create the key
|
|
|
|
|
for err == vfserror.ErrDiskFull && g.gcHanderFunc != nil {
|
|
|
|
|
errGC := g.gcHanderFunc(g.VFS, uint(size)) // call the GC handler
|
|
|
|
|
if errGC == ErrInsufficientSpace {
|
|
|
|
|
return nil, errGC // if the GC handler returns no files to delete, return the error
|
|
|
|
|
}
|
|
|
|
|
w, err = g.VFS.Create(key, size)
|
|
|
|
|
if err == vfserror.ErrDiskFull {
|
|
|
|
|
// GC handler did not free enough space, avoid infinite loop
|
|
|
|
|
return nil, ErrInsufficientSpace
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|