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 }