github.com/dominant-strategies/go-quai@v0.28.2/metrics/meter.go (about)

     1  package metrics
     2  
     3  import (
     4  	"sync"
     5  	"sync/atomic"
     6  	"time"
     7  )
     8  
     9  // Meters count events to produce exponentially-weighted moving average rates
    10  // at one-, five-, and fifteen-minutes and a mean rate.
    11  type Meter interface {
    12  	Count() int64
    13  	Mark(int64)
    14  	Rate1() float64
    15  	Rate5() float64
    16  	Rate15() float64
    17  	RateMean() float64
    18  	Snapshot() Meter
    19  	Stop()
    20  }
    21  
    22  // GetOrRegisterMeter returns an existing Meter or constructs and registers a
    23  // new StandardMeter.
    24  // Be sure to unregister the meter from the registry once it is of no use to
    25  // allow for garbage collection.
    26  func GetOrRegisterMeter(name string, r Registry) Meter {
    27  	if nil == r {
    28  		r = DefaultRegistry
    29  	}
    30  	return r.GetOrRegister(name, NewMeter).(Meter)
    31  }
    32  
    33  // GetOrRegisterMeterForced returns an existing Meter or constructs and registers a
    34  // new StandardMeter no matter the global switch is enabled or not.
    35  // Be sure to unregister the meter from the registry once it is of no use to
    36  // allow for garbage collection.
    37  func GetOrRegisterMeterForced(name string, r Registry) Meter {
    38  	if nil == r {
    39  		r = DefaultRegistry
    40  	}
    41  	return r.GetOrRegister(name, NewMeterForced).(Meter)
    42  }
    43  
    44  // NewMeter constructs a new StandardMeter and launches a goroutine.
    45  // Be sure to call Stop() once the meter is of no use to allow for garbage collection.
    46  func NewMeter() Meter {
    47  	if !Enabled {
    48  		return NilMeter{}
    49  	}
    50  	m := newStandardMeter()
    51  	arbiter.Lock()
    52  	defer arbiter.Unlock()
    53  	arbiter.meters[m] = struct{}{}
    54  	if !arbiter.started {
    55  		arbiter.started = true
    56  		go arbiter.tick()
    57  	}
    58  	return m
    59  }
    60  
    61  // NewMeterForced constructs a new StandardMeter and launches a goroutine no matter
    62  // the global switch is enabled or not.
    63  // Be sure to call Stop() once the meter is of no use to allow for garbage collection.
    64  func NewMeterForced() Meter {
    65  	m := newStandardMeter()
    66  	arbiter.Lock()
    67  	defer arbiter.Unlock()
    68  	arbiter.meters[m] = struct{}{}
    69  	if !arbiter.started {
    70  		arbiter.started = true
    71  		go arbiter.tick()
    72  	}
    73  	return m
    74  }
    75  
    76  // NewRegisteredMeter constructs and registers a new StandardMeter
    77  // and launches a goroutine.
    78  // Be sure to unregister the meter from the registry once it is of no use to
    79  // allow for garbage collection.
    80  func NewRegisteredMeter(name string, r Registry) Meter {
    81  	c := NewMeter()
    82  	if nil == r {
    83  		r = DefaultRegistry
    84  	}
    85  	r.Register(name, c)
    86  	return c
    87  }
    88  
    89  // NewRegisteredMeterForced constructs and registers a new StandardMeter
    90  // and launches a goroutine no matter the global switch is enabled or not.
    91  // Be sure to unregister the meter from the registry once it is of no use to
    92  // allow for garbage collection.
    93  func NewRegisteredMeterForced(name string, r Registry) Meter {
    94  	c := NewMeterForced()
    95  	if nil == r {
    96  		r = DefaultRegistry
    97  	}
    98  	r.Register(name, c)
    99  	return c
   100  }
   101  
   102  // MeterSnapshot is a read-only copy of another Meter.
   103  type MeterSnapshot struct {
   104  	// WARNING: The `temp` field is accessed atomically.
   105  	// On 32 bit platforms, only 64-bit aligned fields can be atomic. The struct is
   106  	// guaranteed to be so aligned, so take advantage of that. For more information,
   107  	// see https://golang.org/pkg/sync/atomic/#pkg-note-BUG.
   108  	temp                           int64
   109  	count                          int64
   110  	rate1, rate5, rate15, rateMean float64
   111  }
   112  
   113  // Count returns the count of events at the time the snapshot was taken.
   114  func (m *MeterSnapshot) Count() int64 { return m.count }
   115  
   116  // Mark panics.
   117  func (*MeterSnapshot) Mark(n int64) {
   118  	panic("Mark called on a MeterSnapshot")
   119  }
   120  
   121  // Rate1 returns the one-minute moving average rate of events per second at the
   122  // time the snapshot was taken.
   123  func (m *MeterSnapshot) Rate1() float64 { return m.rate1 }
   124  
   125  // Rate5 returns the five-minute moving average rate of events per second at
   126  // the time the snapshot was taken.
   127  func (m *MeterSnapshot) Rate5() float64 { return m.rate5 }
   128  
   129  // Rate15 returns the fifteen-minute moving average rate of events per second
   130  // at the time the snapshot was taken.
   131  func (m *MeterSnapshot) Rate15() float64 { return m.rate15 }
   132  
   133  // RateMean returns the meter's mean rate of events per second at the time the
   134  // snapshot was taken.
   135  func (m *MeterSnapshot) RateMean() float64 { return m.rateMean }
   136  
   137  // Snapshot returns the snapshot.
   138  func (m *MeterSnapshot) Snapshot() Meter { return m }
   139  
   140  // Stop is a no-op.
   141  func (m *MeterSnapshot) Stop() {}
   142  
   143  // NilMeter is a no-op Meter.
   144  type NilMeter struct{}
   145  
   146  // Count is a no-op.
   147  func (NilMeter) Count() int64 { return 0 }
   148  
   149  // Mark is a no-op.
   150  func (NilMeter) Mark(n int64) {}
   151  
   152  // Rate1 is a no-op.
   153  func (NilMeter) Rate1() float64 { return 0.0 }
   154  
   155  // Rate5 is a no-op.
   156  func (NilMeter) Rate5() float64 { return 0.0 }
   157  
   158  // Rate15 is a no-op.
   159  func (NilMeter) Rate15() float64 { return 0.0 }
   160  
   161  // RateMean is a no-op.
   162  func (NilMeter) RateMean() float64 { return 0.0 }
   163  
   164  // Snapshot is a no-op.
   165  func (NilMeter) Snapshot() Meter { return NilMeter{} }
   166  
   167  // Stop is a no-op.
   168  func (NilMeter) Stop() {}
   169  
   170  // StandardMeter is the standard implementation of a Meter.
   171  type StandardMeter struct {
   172  	lock        sync.RWMutex
   173  	snapshot    *MeterSnapshot
   174  	a1, a5, a15 EWMA
   175  	startTime   time.Time
   176  	stopped     uint32
   177  }
   178  
   179  func newStandardMeter() *StandardMeter {
   180  	return &StandardMeter{
   181  		snapshot:  &MeterSnapshot{},
   182  		a1:        NewEWMA1(),
   183  		a5:        NewEWMA5(),
   184  		a15:       NewEWMA15(),
   185  		startTime: time.Now(),
   186  	}
   187  }
   188  
   189  // Stop stops the meter, Mark() will be a no-op if you use it after being stopped.
   190  func (m *StandardMeter) Stop() {
   191  	stopped := atomic.SwapUint32(&m.stopped, 1)
   192  	if stopped != 1 {
   193  		arbiter.Lock()
   194  		delete(arbiter.meters, m)
   195  		arbiter.Unlock()
   196  	}
   197  }
   198  
   199  // Count returns the number of events recorded.
   200  // It updates the meter to be as accurate as possible
   201  func (m *StandardMeter) Count() int64 {
   202  	m.lock.Lock()
   203  	defer m.lock.Unlock()
   204  	m.updateMeter()
   205  	return m.snapshot.count
   206  }
   207  
   208  // Mark records the occurrence of n events.
   209  func (m *StandardMeter) Mark(n int64) {
   210  	atomic.AddInt64(&m.snapshot.temp, n)
   211  }
   212  
   213  // Rate1 returns the one-minute moving average rate of events per second.
   214  func (m *StandardMeter) Rate1() float64 {
   215  	m.lock.RLock()
   216  	defer m.lock.RUnlock()
   217  	return m.snapshot.rate1
   218  }
   219  
   220  // Rate5 returns the five-minute moving average rate of events per second.
   221  func (m *StandardMeter) Rate5() float64 {
   222  	m.lock.RLock()
   223  	defer m.lock.RUnlock()
   224  	return m.snapshot.rate5
   225  }
   226  
   227  // Rate15 returns the fifteen-minute moving average rate of events per second.
   228  func (m *StandardMeter) Rate15() float64 {
   229  	m.lock.RLock()
   230  	defer m.lock.RUnlock()
   231  	return m.snapshot.rate15
   232  }
   233  
   234  // RateMean returns the meter's mean rate of events per second.
   235  func (m *StandardMeter) RateMean() float64 {
   236  	m.lock.RLock()
   237  	defer m.lock.RUnlock()
   238  	return m.snapshot.rateMean
   239  }
   240  
   241  // Snapshot returns a read-only copy of the meter.
   242  func (m *StandardMeter) Snapshot() Meter {
   243  	m.lock.RLock()
   244  	snapshot := *m.snapshot
   245  	m.lock.RUnlock()
   246  	return &snapshot
   247  }
   248  
   249  func (m *StandardMeter) updateSnapshot() {
   250  	// should run with write lock held on m.lock
   251  	snapshot := m.snapshot
   252  	snapshot.rate1 = m.a1.Rate()
   253  	snapshot.rate5 = m.a5.Rate()
   254  	snapshot.rate15 = m.a15.Rate()
   255  	snapshot.rateMean = float64(snapshot.count) / time.Since(m.startTime).Seconds()
   256  }
   257  
   258  func (m *StandardMeter) updateMeter() {
   259  	// should only run with write lock held on m.lock
   260  	n := atomic.SwapInt64(&m.snapshot.temp, 0)
   261  	m.snapshot.count += n
   262  	m.a1.Update(n)
   263  	m.a5.Update(n)
   264  	m.a15.Update(n)
   265  }
   266  
   267  func (m *StandardMeter) tick() {
   268  	m.lock.Lock()
   269  	defer m.lock.Unlock()
   270  	m.updateMeter()
   271  	m.a1.Tick()
   272  	m.a5.Tick()
   273  	m.a15.Tick()
   274  	m.updateSnapshot()
   275  }
   276  
   277  // meterArbiter ticks meters every 5s from a single goroutine.
   278  // meters are references in a set for future stopping.
   279  type meterArbiter struct {
   280  	sync.RWMutex
   281  	started bool
   282  	meters  map[*StandardMeter]struct{}
   283  	ticker  *time.Ticker
   284  }
   285  
   286  var arbiter = meterArbiter{ticker: time.NewTicker(5 * time.Second), meters: make(map[*StandardMeter]struct{})}
   287  
   288  // Ticks meters on the scheduled interval
   289  func (ma *meterArbiter) tick() {
   290  	for range ma.ticker.C {
   291  		ma.tickMeters()
   292  	}
   293  }
   294  
   295  func (ma *meterArbiter) tickMeters() {
   296  	ma.RLock()
   297  	defer ma.RUnlock()
   298  	for meter := range ma.meters {
   299  		meter.tick()
   300  	}
   301  }