github.com/MetalBlockchain/subnet-evm@v0.4.9/utils/metered_cache.go (about) 1 // (c) 2022, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package utils 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "sync/atomic" 11 "time" 12 13 "github.com/MetalBlockchain/subnet-evm/metrics" 14 "github.com/VictoriaMetrics/fastcache" 15 "github.com/ethereum/go-ethereum/common" 16 "github.com/ethereum/go-ethereum/log" 17 ) 18 19 // MeteredCache wraps *fastcache.Cache and periodically pulls stats from it. 20 type MeteredCache struct { 21 *fastcache.Cache 22 namespace string 23 24 // stats to be surfaced 25 entriesCount metrics.Gauge 26 bytesSize metrics.Gauge 27 collisions metrics.Gauge 28 gets metrics.Gauge 29 sets metrics.Gauge 30 misses metrics.Gauge 31 statsTime metrics.Gauge 32 33 // count all operations to decide when to update stats 34 ops uint64 35 updateFrequency uint64 36 } 37 38 func dirSize(path string) (int64, error) { 39 var size int64 40 err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { 41 if err != nil { 42 return err 43 } 44 if !info.IsDir() { 45 size += info.Size() 46 } 47 return nil 48 }) 49 return size, err 50 } 51 52 // NewMeteredCache returns a new MeteredCache that will update stats to the 53 // provided namespace once per each [updateFrequency] operations. 54 // Note: if [updateFrequency] is passed as 0, it will be treated as 1. 55 func NewMeteredCache(size int, journal string, namespace string, updateFrequency uint64) *MeteredCache { 56 var cache *fastcache.Cache 57 if journal == "" { 58 cache = fastcache.New(size) 59 } else { 60 dirSize, err := dirSize(journal) 61 log.Info("attempting to load cache from disk", "path", journal, "dirSize", common.StorageSize(dirSize), "err", err) 62 cache = fastcache.LoadFromFileOrNew(journal, size) 63 } 64 if updateFrequency == 0 { 65 updateFrequency = 1 // avoid division by zero 66 } 67 mc := &MeteredCache{ 68 Cache: cache, 69 namespace: namespace, 70 updateFrequency: updateFrequency, 71 } 72 if namespace != "" { 73 // only register stats if a namespace is provided. 74 mc.entriesCount = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/entriesCount", namespace), nil) 75 mc.bytesSize = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/bytesSize", namespace), nil) 76 mc.collisions = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/collisions", namespace), nil) 77 mc.gets = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/gets", namespace), nil) 78 mc.sets = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/sets", namespace), nil) 79 mc.misses = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/misses", namespace), nil) 80 mc.statsTime = metrics.GetOrRegisterGauge(fmt.Sprintf("%s/statsTime", namespace), nil) 81 } 82 return mc 83 } 84 85 // updateStats updates metrics from fastcache 86 func (mc *MeteredCache) updateStatsIfNeeded() { 87 if mc.namespace == "" { 88 return 89 } 90 ops := atomic.AddUint64(&mc.ops, 1) 91 if ops%mc.updateFrequency != 0 { 92 return 93 } 94 95 start := time.Now() 96 s := fastcache.Stats{} 97 mc.UpdateStats(&s) 98 mc.entriesCount.Update(int64(s.EntriesCount)) 99 mc.bytesSize.Update(int64(s.BytesSize)) 100 mc.collisions.Update(int64(s.Collisions)) 101 mc.gets.Update(int64(s.GetCalls)) 102 mc.sets.Update(int64(s.SetCalls)) 103 mc.misses.Update(int64(s.Misses)) 104 mc.statsTime.Inc(int64(time.Since(start))) // cumulative metric 105 } 106 107 func (mc *MeteredCache) Del(k []byte) { 108 mc.updateStatsIfNeeded() 109 mc.Cache.Del(k) 110 } 111 112 func (mc *MeteredCache) Get(dst, k []byte) []byte { 113 mc.updateStatsIfNeeded() 114 return mc.Cache.Get(dst, k) 115 } 116 117 func (mc *MeteredCache) GetBig(dst, k []byte) []byte { 118 mc.updateStatsIfNeeded() 119 return mc.Cache.GetBig(dst, k) 120 } 121 122 func (mc *MeteredCache) Has(k []byte) bool { 123 mc.updateStatsIfNeeded() 124 return mc.Cache.Has(k) 125 } 126 127 func (mc *MeteredCache) HasGet(dst, k []byte) ([]byte, bool) { 128 mc.updateStatsIfNeeded() 129 return mc.Cache.HasGet(dst, k) 130 } 131 132 func (mc *MeteredCache) Set(k, v []byte) { 133 mc.updateStatsIfNeeded() 134 mc.Cache.Set(k, v) 135 } 136 137 func (mc *MeteredCache) SetBig(k, v []byte) { 138 mc.updateStatsIfNeeded() 139 mc.Cache.SetBig(k, v) 140 }