- Replaced SHA1 hash calculations with SHA256 for improved security and consistency in cache key generation. - Introduced a new TestURLHashing function to validate the new cache key generation logic. - Removed outdated hash calculation tests and streamlined the caching process to focus on URL-based hashing. - Implemented lightweight validation methods in ServeHTTP to enhance performance and reliability of cached responses. - Added batched time updates in VFS implementations for better performance during access time tracking.
241 lines
5.9 KiB
Go
241 lines
5.9 KiB
Go
// vfs/gc/gc.go
|
|
package gc
|
|
|
|
import (
|
|
"io"
|
|
"s1d3sw1ped/SteamCache2/vfs"
|
|
"s1d3sw1ped/SteamCache2/vfs/disk"
|
|
"s1d3sw1ped/SteamCache2/vfs/memory"
|
|
)
|
|
|
|
// GCAlgorithm represents different garbage collection strategies
|
|
type GCAlgorithm string
|
|
|
|
const (
|
|
LRU GCAlgorithm = "lru"
|
|
LFU GCAlgorithm = "lfu"
|
|
FIFO GCAlgorithm = "fifo"
|
|
Largest GCAlgorithm = "largest"
|
|
Smallest GCAlgorithm = "smallest"
|
|
Hybrid GCAlgorithm = "hybrid"
|
|
)
|
|
|
|
// GCFS wraps a VFS with garbage collection capabilities
|
|
type GCFS struct {
|
|
vfs vfs.VFS
|
|
algorithm GCAlgorithm
|
|
gcFunc func(vfs.VFS, uint) uint
|
|
}
|
|
|
|
// New creates a new GCFS with the specified algorithm
|
|
func New(wrappedVFS vfs.VFS, algorithm GCAlgorithm) *GCFS {
|
|
gcfs := &GCFS{
|
|
vfs: wrappedVFS,
|
|
algorithm: algorithm,
|
|
}
|
|
|
|
switch algorithm {
|
|
case LRU:
|
|
gcfs.gcFunc = gcLRU
|
|
case LFU:
|
|
gcfs.gcFunc = gcLFU
|
|
case FIFO:
|
|
gcfs.gcFunc = gcFIFO
|
|
case Largest:
|
|
gcfs.gcFunc = gcLargest
|
|
case Smallest:
|
|
gcfs.gcFunc = gcSmallest
|
|
case Hybrid:
|
|
gcfs.gcFunc = gcHybrid
|
|
default:
|
|
// Default to LRU
|
|
gcfs.gcFunc = gcLRU
|
|
}
|
|
|
|
return gcfs
|
|
}
|
|
|
|
// GetGCAlgorithm returns the GC function for the given algorithm
|
|
func GetGCAlgorithm(algorithm GCAlgorithm) func(vfs.VFS, uint) uint {
|
|
switch algorithm {
|
|
case LRU:
|
|
return gcLRU
|
|
case LFU:
|
|
return gcLFU
|
|
case FIFO:
|
|
return gcFIFO
|
|
case Largest:
|
|
return gcLargest
|
|
case Smallest:
|
|
return gcSmallest
|
|
case Hybrid:
|
|
return gcHybrid
|
|
default:
|
|
return gcLRU
|
|
}
|
|
}
|
|
|
|
// Create wraps the underlying Create method
|
|
func (gc *GCFS) Create(key string, size int64) (io.WriteCloser, error) {
|
|
// Check if we need to GC before creating
|
|
if gc.vfs.Size()+size > gc.vfs.Capacity() {
|
|
needed := uint((gc.vfs.Size() + size) - gc.vfs.Capacity())
|
|
gc.gcFunc(gc.vfs, needed)
|
|
}
|
|
|
|
return gc.vfs.Create(key, size)
|
|
}
|
|
|
|
// Open wraps the underlying Open method
|
|
func (gc *GCFS) Open(key string) (io.ReadCloser, error) {
|
|
return gc.vfs.Open(key)
|
|
}
|
|
|
|
// Delete wraps the underlying Delete method
|
|
func (gc *GCFS) Delete(key string) error {
|
|
return gc.vfs.Delete(key)
|
|
}
|
|
|
|
// Stat wraps the underlying Stat method
|
|
func (gc *GCFS) Stat(key string) (*vfs.FileInfo, error) {
|
|
return gc.vfs.Stat(key)
|
|
}
|
|
|
|
// Name wraps the underlying Name method
|
|
func (gc *GCFS) Name() string {
|
|
return gc.vfs.Name() + "(GC:" + string(gc.algorithm) + ")"
|
|
}
|
|
|
|
// Size wraps the underlying Size method
|
|
func (gc *GCFS) Size() int64 {
|
|
return gc.vfs.Size()
|
|
}
|
|
|
|
// Capacity wraps the underlying Capacity method
|
|
func (gc *GCFS) Capacity() int64 {
|
|
return gc.vfs.Capacity()
|
|
}
|
|
|
|
// EvictionStrategy defines an interface for cache eviction
|
|
type EvictionStrategy interface {
|
|
Evict(vfs vfs.VFS, bytesNeeded uint) uint
|
|
}
|
|
|
|
// GC functions
|
|
|
|
// gcLRU implements Least Recently Used eviction
|
|
func gcLRU(v vfs.VFS, bytesNeeded uint) uint {
|
|
return evictLRU(v, bytesNeeded)
|
|
}
|
|
|
|
// gcLFU implements Least Frequently Used eviction
|
|
func gcLFU(v vfs.VFS, bytesNeeded uint) uint {
|
|
return evictLFU(v, bytesNeeded)
|
|
}
|
|
|
|
// gcFIFO implements First In First Out eviction
|
|
func gcFIFO(v vfs.VFS, bytesNeeded uint) uint {
|
|
return evictFIFO(v, bytesNeeded)
|
|
}
|
|
|
|
// gcLargest implements largest file first eviction
|
|
func gcLargest(v vfs.VFS, bytesNeeded uint) uint {
|
|
return evictLargest(v, bytesNeeded)
|
|
}
|
|
|
|
// gcSmallest implements smallest file first eviction
|
|
func gcSmallest(v vfs.VFS, bytesNeeded uint) uint {
|
|
return evictSmallest(v, bytesNeeded)
|
|
}
|
|
|
|
// gcHybrid implements a hybrid eviction strategy
|
|
func gcHybrid(v vfs.VFS, bytesNeeded uint) uint {
|
|
return evictHybrid(v, bytesNeeded)
|
|
}
|
|
|
|
// evictLRU performs LRU eviction by removing least recently used files
|
|
func evictLRU(v vfs.VFS, bytesNeeded uint) uint {
|
|
// Try to use specific eviction methods if available
|
|
switch fs := v.(type) {
|
|
case *memory.MemoryFS:
|
|
return fs.EvictLRU(bytesNeeded)
|
|
case *disk.DiskFS:
|
|
return fs.EvictLRU(bytesNeeded)
|
|
default:
|
|
// No fallback - return 0 (no eviction performed)
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// evictLFU performs LFU (Least Frequently Used) eviction
|
|
func evictLFU(v vfs.VFS, bytesNeeded uint) uint {
|
|
// For now, fall back to size-based eviction
|
|
// TODO: Implement proper LFU tracking
|
|
return evictBySize(v, bytesNeeded)
|
|
}
|
|
|
|
// evictFIFO performs FIFO (First In First Out) eviction
|
|
func evictFIFO(v vfs.VFS, bytesNeeded uint) uint {
|
|
switch fs := v.(type) {
|
|
case *memory.MemoryFS:
|
|
return fs.EvictFIFO(bytesNeeded)
|
|
case *disk.DiskFS:
|
|
return fs.EvictFIFO(bytesNeeded)
|
|
default:
|
|
// No fallback - return 0 (no eviction performed)
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// evictLargest evicts largest files first
|
|
func evictLargest(v vfs.VFS, bytesNeeded uint) uint {
|
|
return evictBySizeDesc(v, bytesNeeded)
|
|
}
|
|
|
|
// evictSmallest evicts smallest files first
|
|
func evictSmallest(v vfs.VFS, bytesNeeded uint) uint {
|
|
return evictBySizeAsc(v, bytesNeeded)
|
|
}
|
|
|
|
// evictBySize evicts files based on size (smallest first)
|
|
func evictBySize(v vfs.VFS, bytesNeeded uint) uint {
|
|
return evictBySizeAsc(v, bytesNeeded)
|
|
}
|
|
|
|
// evictBySizeAsc evicts smallest files first
|
|
func evictBySizeAsc(v vfs.VFS, bytesNeeded uint) uint {
|
|
switch fs := v.(type) {
|
|
case *memory.MemoryFS:
|
|
return fs.EvictBySize(bytesNeeded, true) // true = ascending (smallest first)
|
|
case *disk.DiskFS:
|
|
return fs.EvictBySize(bytesNeeded, true) // true = ascending (smallest first)
|
|
default:
|
|
// No fallback - return 0 (no eviction performed)
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// evictBySizeDesc evicts largest files first
|
|
func evictBySizeDesc(v vfs.VFS, bytesNeeded uint) uint {
|
|
switch fs := v.(type) {
|
|
case *memory.MemoryFS:
|
|
return fs.EvictBySize(bytesNeeded, false) // false = descending (largest first)
|
|
case *disk.DiskFS:
|
|
return fs.EvictBySize(bytesNeeded, false) // false = descending (largest first)
|
|
default:
|
|
// No fallback - return 0 (no eviction performed)
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// evictHybrid implements a hybrid eviction strategy
|
|
func evictHybrid(v vfs.VFS, bytesNeeded uint) uint {
|
|
// Use LRU as primary strategy, but consider size as tiebreaker
|
|
return evictLRU(v, bytesNeeded)
|
|
}
|
|
|
|
// AdaptivePromotionDeciderFunc is a placeholder for the adaptive promotion logic
|
|
var AdaptivePromotionDeciderFunc = func() interface{} {
|
|
return nil
|
|
}
|