Update metrics tracking and enhance cache eviction strategies
Release Tag / release (push) Successful in 34s
Release Tag / release (push) Successful in 34s
- Added metrics for bytes saved from cache to improve performance insights. - Updated cache eviction strategies in MemoryFS and DiskFS to include metrics tracking for hits and evictions. - Enhanced README.md with updated garbage collection algorithm descriptions and recommendations for cache usage. - Introduced new madviseSequential functionality for improved memory access hints on Unix systems. - Adjusted validation configuration in examples to better reflect realistic usage scenarios.
This commit is contained in:
+51
-2
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"s1d3sw1ped/steamcache2/steamcache/logger"
|
||||
"s1d3sw1ped/steamcache2/steamcache/metrics"
|
||||
"s1d3sw1ped/steamcache2/vfs"
|
||||
"s1d3sw1ped/steamcache2/vfs/locks"
|
||||
"s1d3sw1ped/steamcache2/vfs/lru"
|
||||
@@ -43,6 +44,7 @@ type DiskFS struct {
|
||||
// initCloseOnce ensures initDone closed exactly once even on panic in bg populator (panic safety for Issue 1).
|
||||
initCloseOnce sync.Once
|
||||
startupEvict func(vfs.VFS, uint) uint // passed to New (via gc.GetGCAlgorithm); invoked as last step of bg init if over cap (no post-ctor race)
|
||||
metrics *metrics.Metrics
|
||||
}
|
||||
|
||||
// shardPath converts a Steam cache key to a sharded directory path to reduce inode pressure
|
||||
@@ -134,6 +136,12 @@ func New(root string, capacity int64, evict func(vfs.VFS, uint) uint) (*DiskFS,
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// SetMetrics allows the owner (SteamCache) to inject the metrics collector
|
||||
// so that per-tier hit and eviction counters can be recorded.
|
||||
func (d *DiskFS) SetMetrics(met *metrics.Metrics) {
|
||||
d.metrics = met
|
||||
}
|
||||
|
||||
// calculateSizeAndPopulateIndex runs in background from New to avoid blocking startup or O(N) RAM for large caches (millions of Steam files).
|
||||
// It streams batch inserts (bounded by maxEvictBatch) to keep lock times short and eliminate giant temporary slice.
|
||||
// Startup over-capacity eviction (if needed) runs as the very last step (using the evict func passed to New, selected via gc.GetGCAlgorithm).
|
||||
@@ -489,8 +497,14 @@ func (d *DiskFS) Open(key string) (io.ReadCloser, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Use memory mapping for large files (>1MB) to improve performance
|
||||
const mmapThreshold = 1024 * 1024 // 1MB
|
||||
// Use memory mapping for large files to improve performance.
|
||||
// We use 8 MiB as the threshold because:
|
||||
// - Most Steam chunks are ~1 MiB (see current disk cache analysis).
|
||||
// - mmap has non-trivial fixed overhead (page tables, TLB, faults).
|
||||
// - For files < ~4-8 MiB the overhead often outweighs the zero-copy benefit
|
||||
// on mostly sequential access patterns.
|
||||
// - Larger files benefit more from kernel readahead + zero-copy.
|
||||
const mmapThreshold = 8 * 1024 * 1024 // 8 MiB
|
||||
if fi.Size > mmapThreshold {
|
||||
// Close the regular file handle
|
||||
_ = file.Close() // best-effort; mmap path takes over or falls back
|
||||
@@ -505,9 +519,21 @@ func (d *DiskFS) Open(key string) (io.ReadCloser, error) {
|
||||
if err != nil {
|
||||
_ = mmapFile.Close() // best-effort close before fallback open
|
||||
// Fallback to regular file reading (intentional 3rd open of same path after mmap failure; pre-existing pattern, no leak)
|
||||
if d.metrics != nil {
|
||||
d.metrics.IncrementDiskCacheHits()
|
||||
}
|
||||
return os.Open(path)
|
||||
}
|
||||
|
||||
// Hint to the kernel (on supported platforms) that we will access
|
||||
// this mapping sequentially. This enables better readahead.
|
||||
if err := madviseSequential(mapped); err != nil {
|
||||
logger.Logger.Debug().
|
||||
Err(err).
|
||||
Str("key", key).
|
||||
Msg("madvise(MADV_SEQUENTIAL) failed on mmap'd chunk")
|
||||
}
|
||||
|
||||
return &mmapReadCloser{
|
||||
data: mapped,
|
||||
file: mmapFile,
|
||||
@@ -515,6 +541,9 @@ func (d *DiskFS) Open(key string) (io.ReadCloser, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
if d.metrics != nil {
|
||||
d.metrics.IncrementDiskCacheHits()
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
@@ -677,6 +706,10 @@ func (d *DiskFS) EvictLRU(bytesNeeded uint) uint {
|
||||
}
|
||||
}
|
||||
d.mu.Unlock()
|
||||
|
||||
if d.metrics != nil && evicted > 0 {
|
||||
d.metrics.IncrementEvictions()
|
||||
}
|
||||
return evicted
|
||||
}
|
||||
|
||||
@@ -726,6 +759,10 @@ func (d *DiskFS) EvictBySize(bytesNeeded uint, ascending bool) uint {
|
||||
}
|
||||
}
|
||||
d.mu.Unlock()
|
||||
|
||||
if d.metrics != nil && evicted > 0 {
|
||||
d.metrics.IncrementEvictions()
|
||||
}
|
||||
return evicted
|
||||
}
|
||||
|
||||
@@ -773,6 +810,10 @@ func (d *DiskFS) EvictFIFO(bytesNeeded uint) uint {
|
||||
}
|
||||
}
|
||||
d.mu.Unlock()
|
||||
|
||||
if d.metrics != nil && evicted > 0 {
|
||||
d.metrics.IncrementEvictions()
|
||||
}
|
||||
return evicted
|
||||
}
|
||||
|
||||
@@ -825,6 +866,10 @@ func (d *DiskFS) EvictLFU(bytesNeeded uint) uint {
|
||||
}
|
||||
}
|
||||
d.mu.Unlock()
|
||||
|
||||
if d.metrics != nil && evicted > 0 {
|
||||
d.metrics.IncrementEvictions()
|
||||
}
|
||||
return evicted
|
||||
}
|
||||
|
||||
@@ -878,5 +923,9 @@ func (d *DiskFS) EvictHybrid(bytesNeeded uint) uint {
|
||||
}
|
||||
}
|
||||
d.mu.Unlock()
|
||||
|
||||
if d.metrics != nil && evicted > 0 {
|
||||
d.metrics.IncrementEvictions()
|
||||
}
|
||||
return evicted
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user