diff --git a/steamcache/avgcachestate/avgcachestate.go b/steamcache/avgcachestate/avgcachestate.go new file mode 100644 index 0000000..5a6869e --- /dev/null +++ b/steamcache/avgcachestate/avgcachestate.go @@ -0,0 +1,60 @@ +package avgcachestate + +import ( + "s1d3sw1ped/SteamCache2/vfs/cachestate" + "sync" +) + +// AvgCacheState is a cache state that averages the last N cache states. +type AvgCacheState struct { + size int + avgs []cachestate.CacheState + mu sync.Mutex +} + +// New creates a new average cache state with the given size. +func New(size int) *AvgCacheState { + return &AvgCacheState{ + size: size, + avgs: make([]cachestate.CacheState, size), + mu: sync.Mutex{}, + } +} + +// Clear resets the average cache state to zero. +func (a *AvgCacheState) Clear() { + a.mu.Lock() + defer a.mu.Unlock() + + a.avgs = make([]cachestate.CacheState, a.size) // zeroed +} + +// Add adds a cache state to the average cache state. +func (a *AvgCacheState) Add(cs cachestate.CacheState) { + a.mu.Lock() + defer a.mu.Unlock() + + a.avgs = append(a.avgs, cs) + if len(a.avgs) > a.size { + a.avgs = a.avgs[1:] + } +} + +// Avg returns the average cache state. +func (a *AvgCacheState) Avg() float64 { + a.mu.Lock() + defer a.mu.Unlock() + + var hits, misses int + for _, cs := range a.avgs { + switch cs { + case cachestate.CacheStateHit: + hits++ + case cachestate.CacheStateMiss: + misses++ + } + } + total := hits + misses + + return float64(hits) / float64(total) +} diff --git a/steamcache/steamcache.go b/steamcache/steamcache.go index cefbfdb..fee26d5 100644 --- a/steamcache/steamcache.go +++ b/steamcache/steamcache.go @@ -5,8 +5,10 @@ import ( "log" "net/http" "net/url" + "s1d3sw1ped/SteamCache2/steamcache/avgcachestate" "s1d3sw1ped/SteamCache2/vfs" "s1d3sw1ped/SteamCache2/vfs/cache" + "s1d3sw1ped/SteamCache2/vfs/cachestate" "s1d3sw1ped/SteamCache2/vfs/disk" "s1d3sw1ped/SteamCache2/vfs/gc" "s1d3sw1ped/SteamCache2/vfs/memory" @@ -25,6 +27,8 @@ type SteamCache struct { memory *memory.MemoryFS disk *disk.DiskFS + hits *avgcachestate.AvgCacheState + dirty bool mu sync.Mutex } @@ -98,16 +102,16 @@ func (sc *SteamCache) LogStats() { defer sc.mu.Unlock() if sc.dirty { log.Printf( - "SteamCache2 %s: (%d) %s/%s %s: (%d) %s/%s", + "SteamCache2 %s: (%d) %s/%s %s: (%d) %s/%s Hitrate: %f%%", sc.memory.Name(), len(sc.memory.StatAll()), units.HumanSize(float64(sc.memory.Size())), units.HumanSize(float64(sc.memory.Capacity())), sc.disk.Name(), len(sc.disk.StatAll()), units.HumanSize(float64(sc.disk.Size())), units.HumanSize(float64(sc.disk.Capacity())), + sc.hits.Avg()*100, ) sc.dirty = false } } func (sc *SteamCache) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { http.Error(w, "Only GET method is supported", http.StatusMethodNotAllowed) return @@ -148,6 +152,7 @@ func (sc *SteamCache) ServeHTTP(w http.ResponseWriter, r *http.Request) { data, err := sc.vfs.Get(cacheKey) if err == nil { + sc.hits.Add(cachestate.CacheStateHit) w.Header().Add("X-LanCache-Status", "HIT") w.Write(data) return @@ -185,6 +190,7 @@ func (sc *SteamCache) ServeHTTP(w http.ResponseWriter, r *http.Request) { } sc.vfs.Set(cacheKey, body) + sc.hits.Add(cachestate.CacheStateMiss) w.Header().Add("X-LanCache-Status", "MISS") w.Write(body) } @@ -223,6 +229,5 @@ func forward(w http.ResponseWriter, r *http.Request) { return } - w.Header().Add("X-LanCache-Status", "MISS") w.Write(body) }