All checks were successful
Release Tag / release (push) Successful in 9s
- Updated the caching logic to utilize a predictive cache warmer, enhancing content prefetching based on access patterns. - Replaced the legacy warming system with a more efficient predictive approach, allowing for better performance and resource management. - Refactored memory management to integrate dynamic cache size adjustments based on system memory usage, improving overall efficiency. - Simplified the VFS interface and improved concurrency handling with sharded locks for better performance in multi-threaded environments. - Enhanced tests to validate the new caching and memory management behaviors, ensuring reliability and performance improvements.
223 lines
5.1 KiB
Go
223 lines
5.1 KiB
Go
// 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()
|
|
}
|
|
}
|
|
}
|
|
}
|