github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/ldbutils/count_meter.go (about)

     1  package ldbutils
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	metrics "github.com/rcrowley/go-metrics"
     8  )
     9  
    10  // MeterStatus represents the status of a rate meter.
    11  type MeterStatus struct {
    12  	Minutes1  float64
    13  	Minutes5  float64
    14  	Minutes15 float64
    15  	Count     int64
    16  }
    17  
    18  // RateMeterToStatus returns the status for a meter.
    19  func RateMeterToStatus(m metrics.Meter) MeterStatus {
    20  	s := m.Snapshot()
    21  	return MeterStatus{
    22  		s.Rate1(),
    23  		s.Rate5(),
    24  		s.Rate15(),
    25  		s.Count(),
    26  	}
    27  }
    28  
    29  // CountMeter counts ticks with a sliding window into the past.
    30  type CountMeter struct {
    31  	mtx      sync.RWMutex
    32  	counters []int64
    33  	overall  int64
    34  
    35  	shutdownCh chan struct{}
    36  }
    37  
    38  var _ metrics.Meter = (*CountMeter)(nil)
    39  
    40  func (m *CountMeter) tick() {
    41  	m.mtx.Lock()
    42  	defer m.mtx.Unlock()
    43  	for i := len(m.counters) - 1; i > 0; i-- {
    44  		m.counters[i] = m.counters[i-1]
    45  	}
    46  	m.counters[0] = 0
    47  }
    48  
    49  func (m *CountMeter) run() {
    50  	ticker := time.NewTicker(time.Minute)
    51  	for {
    52  		select {
    53  		case <-m.shutdownCh:
    54  			return
    55  		case <-ticker.C:
    56  			m.tick()
    57  		}
    58  	}
    59  }
    60  
    61  // NewCountMeter returns a new CountMeter.
    62  func NewCountMeter() *CountMeter {
    63  	m := &CountMeter{
    64  		counters:   make([]int64, 16),
    65  		shutdownCh: make(chan struct{}),
    66  	}
    67  	go m.run()
    68  
    69  	return m
    70  }
    71  
    72  // Count returns the overall count.
    73  func (m *CountMeter) Count() int64 {
    74  	m.mtx.RLock()
    75  	defer m.mtx.RUnlock()
    76  	return m.overall
    77  }
    78  
    79  // Mark ticks the counters.
    80  func (m *CountMeter) Mark(i int64) {
    81  	m.mtx.Lock()
    82  	defer m.mtx.Unlock()
    83  	m.counters[0] += i
    84  	m.overall += i
    85  }
    86  
    87  func (m *CountMeter) rateN(n int) float64 {
    88  	var count int64
    89  	for i := 0; i < n; i++ {
    90  		count += m.counters[i]
    91  	}
    92  	return float64(count)
    93  }
    94  
    95  func (m *CountMeter) rate1() float64 {
    96  	return float64(m.counters[0])
    97  }
    98  
    99  func (m *CountMeter) rate5() float64 {
   100  	return m.rateN(5)
   101  }
   102  
   103  func (m *CountMeter) rate15() float64 {
   104  	return m.rateN(15)
   105  }
   106  
   107  func (m *CountMeter) rateMean() float64 {
   108  	return float64(m.overall)
   109  }
   110  
   111  // Rate1 returns the number of ticks in the last 1 minute.
   112  func (m *CountMeter) Rate1() float64 {
   113  	m.mtx.RLock()
   114  	defer m.mtx.RUnlock()
   115  	return m.rate1()
   116  }
   117  
   118  // Rate5 returns the number of ticks in the last 5 minutes.
   119  func (m *CountMeter) Rate5() float64 {
   120  	m.mtx.RLock()
   121  	defer m.mtx.RUnlock()
   122  	return m.rate5()
   123  }
   124  
   125  // Rate15 returns the number of ticks in the last 15 minutes.
   126  func (m *CountMeter) Rate15() float64 {
   127  	m.mtx.RLock()
   128  	defer m.mtx.RUnlock()
   129  	return m.rate15()
   130  }
   131  
   132  // RateMean returns the overall count of ticks.
   133  func (m *CountMeter) RateMean() float64 {
   134  	m.mtx.RLock()
   135  	defer m.mtx.RUnlock()
   136  	return m.rateMean()
   137  }
   138  
   139  // Snapshot returns the snapshot in time of this CountMeter.
   140  func (m *CountMeter) Snapshot() metrics.Meter {
   141  	m.mtx.RLock()
   142  	defer m.mtx.RUnlock()
   143  	return &MeterSnapshot{
   144  		m.overall,
   145  		m.rate1(),
   146  		m.rate5(),
   147  		m.rate15(),
   148  		m.rateMean(),
   149  	}
   150  }
   151  
   152  // Shutdown shuts down this CountMeter.
   153  func (m *CountMeter) Shutdown() <-chan struct{} {
   154  	select {
   155  	case <-m.shutdownCh:
   156  	default:
   157  		close(m.shutdownCh)
   158  	}
   159  	return m.shutdownCh
   160  }