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  }