github.com/ethereum/go-ethereum@v1.16.1/metrics/meter.go (about)

     1  package metrics
     2  
     3  import (
     4  	"math"
     5  	"sync"
     6  	"sync/atomic"
     7  	"time"
     8  )
     9  
    10  // GetOrRegisterMeter returns an existing Meter or constructs and registers a
    11  // new Meter.
    12  // Be sure to unregister the meter from the registry once it is of no use to
    13  // allow for garbage collection.
    14  func GetOrRegisterMeter(name string, r Registry) *Meter {
    15  	return getOrRegister(name, NewMeter, r)
    16  }
    17  
    18  // NewMeter constructs a new Meter and launches a goroutine.
    19  // Be sure to call Stop() once the meter is of no use to allow for garbage collection.
    20  func NewMeter() *Meter {
    21  	m := newMeter()
    22  	arbiter.add(m)
    23  	return m
    24  }
    25  
    26  // NewInactiveMeter returns a meter but does not start any goroutines. This
    27  // method is mainly intended for testing.
    28  func NewInactiveMeter() *Meter {
    29  	return newMeter()
    30  }
    31  
    32  // NewRegisteredMeter constructs and registers a new Meter
    33  // and launches a goroutine.
    34  // Be sure to unregister the meter from the registry once it is of no use to
    35  // allow for garbage collection.
    36  func NewRegisteredMeter(name string, r Registry) *Meter {
    37  	return GetOrRegisterMeter(name, r)
    38  }
    39  
    40  // MeterSnapshot is a read-only copy of the meter's internal values.
    41  type MeterSnapshot struct {
    42  	count                          int64
    43  	rate1, rate5, rate15, rateMean float64
    44  }
    45  
    46  // Count returns the count of events at the time the snapshot was taken.
    47  func (m *MeterSnapshot) Count() int64 { return m.count }
    48  
    49  // Rate1 returns the one-minute moving average rate of events per second at the
    50  // time the snapshot was taken.
    51  func (m *MeterSnapshot) Rate1() float64 { return m.rate1 }
    52  
    53  // Rate5 returns the five-minute moving average rate of events per second at
    54  // the time the snapshot was taken.
    55  func (m *MeterSnapshot) Rate5() float64 { return m.rate5 }
    56  
    57  // Rate15 returns the fifteen-minute moving average rate of events per second
    58  // at the time the snapshot was taken.
    59  func (m *MeterSnapshot) Rate15() float64 { return m.rate15 }
    60  
    61  // RateMean returns the meter's mean rate of events per second at the time the
    62  // snapshot was taken.
    63  func (m *MeterSnapshot) RateMean() float64 { return m.rateMean }
    64  
    65  // Meter count events to produce exponentially-weighted moving average rates
    66  // at one-, five-, and fifteen-minutes and a mean rate.
    67  type Meter struct {
    68  	count     atomic.Int64
    69  	uncounted atomic.Int64 // not yet added to the EWMAs
    70  	rateMean  atomic.Uint64
    71  
    72  	a1, a5, a15 *EWMA
    73  	startTime   time.Time
    74  	stopped     atomic.Bool
    75  }
    76  
    77  func newMeter() *Meter {
    78  	return &Meter{
    79  		a1:        NewEWMA1(),
    80  		a5:        NewEWMA5(),
    81  		a15:       NewEWMA15(),
    82  		startTime: time.Now(),
    83  	}
    84  }
    85  
    86  // Stop stops the meter, Mark() will be a no-op if you use it after being stopped.
    87  func (m *Meter) Stop() {
    88  	if stopped := m.stopped.Swap(true); !stopped {
    89  		arbiter.remove(m)
    90  	}
    91  }
    92  
    93  // Mark records the occurrence of n events.
    94  func (m *Meter) Mark(n int64) {
    95  	m.uncounted.Add(n)
    96  }
    97  
    98  // Snapshot returns a read-only copy of the meter.
    99  func (m *Meter) Snapshot() *MeterSnapshot {
   100  	return &MeterSnapshot{
   101  		count:    m.count.Load() + m.uncounted.Load(),
   102  		rate1:    m.a1.Snapshot().Rate(),
   103  		rate5:    m.a5.Snapshot().Rate(),
   104  		rate15:   m.a15.Snapshot().Rate(),
   105  		rateMean: math.Float64frombits(m.rateMean.Load()),
   106  	}
   107  }
   108  
   109  func (m *Meter) tick() {
   110  	// Take the uncounted values, add to count
   111  	n := m.uncounted.Swap(0)
   112  	count := m.count.Add(n)
   113  	m.rateMean.Store(math.Float64bits(float64(count) / time.Since(m.startTime).Seconds()))
   114  	// Update the EWMA's internal state
   115  	m.a1.Update(n)
   116  	m.a5.Update(n)
   117  	m.a15.Update(n)
   118  	// And trigger them to calculate the rates
   119  	m.a1.tick()
   120  	m.a5.tick()
   121  	m.a15.tick()
   122  }
   123  
   124  var arbiter = meterTicker{meters: make(map[*Meter]struct{})}
   125  
   126  // meterTicker ticks meters every 5s from a single goroutine.
   127  // meters are references in a set for future stopping.
   128  type meterTicker struct {
   129  	mu sync.RWMutex
   130  
   131  	once   sync.Once
   132  	meters map[*Meter]struct{}
   133  }
   134  
   135  // add a *Meter to the arbiter
   136  func (ma *meterTicker) add(m *Meter) {
   137  	ma.mu.Lock()
   138  	defer ma.mu.Unlock()
   139  	ma.meters[m] = struct{}{}
   140  }
   141  
   142  // remove removes a meter from the set of ticked meters.
   143  func (ma *meterTicker) remove(m *Meter) {
   144  	ma.mu.Lock()
   145  	delete(ma.meters, m)
   146  	ma.mu.Unlock()
   147  }
   148  
   149  // loop ticks meters on a 5-second interval.
   150  func (ma *meterTicker) loop() {
   151  	ticker := time.NewTicker(5 * time.Second)
   152  	for range ticker.C {
   153  		if !metricsEnabled {
   154  			continue
   155  		}
   156  		ma.mu.RLock()
   157  		for meter := range ma.meters {
   158  			meter.tick()
   159  		}
   160  		ma.mu.RUnlock()
   161  	}
   162  }
   163  
   164  // startMeterTickerLoop will start the arbiter ticker.
   165  func startMeterTickerLoop() {
   166  	arbiter.once.Do(func() { go arbiter.loop() })
   167  }