// vfs/cache/cache.go package cache import ( "io" "s1d3sw1ped/steamcache2/vfs" "s1d3sw1ped/steamcache2/vfs/vfserror" "sync/atomic" ) // TieredCache implements a lock-free two-tier cache for better concurrency type TieredCache struct { fast *atomic.Value // Memory cache (fast) - atomic.Value for lock-free access slow *atomic.Value // Disk cache (slow) - atomic.Value for lock-free access } // New creates a new tiered cache func New() *TieredCache { return &TieredCache{ fast: &atomic.Value{}, slow: &atomic.Value{}, } } // SetFast sets the fast (memory) tier atomically func (tc *TieredCache) SetFast(vfs vfs.VFS) { tc.fast.Store(vfs) } // SetSlow sets the slow (disk) tier atomically func (tc *TieredCache) SetSlow(vfs vfs.VFS) { tc.slow.Store(vfs) } // Create creates a new file, preferring the slow tier for persistence func (tc *TieredCache) Create(key string, size int64) (io.WriteCloser, error) { // Try slow tier first (disk) for better testability if slow := tc.slow.Load(); slow != nil { if vfs, ok := slow.(vfs.VFS); ok { return vfs.Create(key, size) } } // Fall back to fast tier (memory) if fast := tc.fast.Load(); fast != nil { if vfs, ok := fast.(vfs.VFS); ok { return vfs.Create(key, size) } } return nil, vfserror.ErrNotFound } // Open opens a file, checking fast tier first, then slow tier with promotion func (tc *TieredCache) Open(key string) (io.ReadCloser, error) { // Try fast tier first (memory) if fast := tc.fast.Load(); fast != nil { if vfs, ok := fast.(vfs.VFS); ok { if reader, err := vfs.Open(key); err == nil { return reader, nil } } } // Fall back to slow tier (disk) and promote to fast tier if slow := tc.slow.Load(); slow != nil { if vfs, ok := slow.(vfs.VFS); ok { reader, err := vfs.Open(key) if err != nil { return nil, err } // If we have both tiers, promote the file to fast tier if fast := tc.fast.Load(); fast != nil { // Create a new reader for promotion to avoid interfering with the returned reader promotionReader, err := vfs.Open(key) if err == nil { go tc.promoteToFast(key, promotionReader) } } return reader, nil } } return nil, vfserror.ErrNotFound } // Delete removes a file from all tiers func (tc *TieredCache) Delete(key string) error { var lastErr error // Delete from fast tier if fast := tc.fast.Load(); fast != nil { if vfs, ok := fast.(vfs.VFS); ok { if err := vfs.Delete(key); err != nil { lastErr = err } } } // Delete from slow tier if slow := tc.slow.Load(); slow != nil { if vfs, ok := slow.(vfs.VFS); ok { if err := vfs.Delete(key); err != nil { lastErr = err } } } return lastErr } // Stat returns file information, checking fast tier first func (tc *TieredCache) Stat(key string) (*vfs.FileInfo, error) { // Try fast tier first (memory) if fast := tc.fast.Load(); fast != nil { if vfs, ok := fast.(vfs.VFS); ok { if info, err := vfs.Stat(key); err == nil { return info, nil } } } // Fall back to slow tier (disk) if slow := tc.slow.Load(); slow != nil { if vfs, ok := slow.(vfs.VFS); ok { return vfs.Stat(key) } } return nil, vfserror.ErrNotFound } // Name returns the cache name func (tc *TieredCache) Name() string { return "TieredCache" } // Size returns the total size across all tiers func (tc *TieredCache) Size() int64 { var total int64 if fast := tc.fast.Load(); fast != nil { if vfs, ok := fast.(vfs.VFS); ok { total += vfs.Size() } } if slow := tc.slow.Load(); slow != nil { if vfs, ok := slow.(vfs.VFS); ok { total += vfs.Size() } } return total } // Capacity returns the total capacity across all tiers func (tc *TieredCache) Capacity() int64 { var total int64 if fast := tc.fast.Load(); fast != nil { if vfs, ok := fast.(vfs.VFS); ok { total += vfs.Capacity() } } if slow := tc.slow.Load(); slow != nil { if vfs, ok := slow.(vfs.VFS); ok { total += vfs.Capacity() } } return total } // promoteToFast promotes a file from slow tier to fast tier func (tc *TieredCache) promoteToFast(key string, reader io.ReadCloser) { defer reader.Close() // Get file info from slow tier to determine size var size int64 if slow := tc.slow.Load(); slow != nil { if vfs, ok := slow.(vfs.VFS); ok { if info, err := vfs.Stat(key); err == nil { size = info.Size } else { return // Skip promotion if we can't get file info } } } // Check if file fits in available memory cache space if fast := tc.fast.Load(); fast != nil { if vfs, ok := fast.(vfs.VFS); ok { availableSpace := vfs.Capacity() - vfs.Size() // Only promote if file fits in available space (with 10% buffer for safety) if size > int64(float64(availableSpace)*0.9) { return // Skip promotion if file is too large } } } // Read the entire file content content, err := io.ReadAll(reader) if err != nil { return // Skip promotion if read fails } // Create the file in fast tier if fast := tc.fast.Load(); fast != nil { if vfs, ok := fast.(vfs.VFS); ok { writer, err := vfs.Create(key, size) if err == nil { // Write content to fast tier writer.Write(content) writer.Close() } } } }