gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/common/metrics/resetting_timer.go (about) 1 // Copyright 2018 The aquachain Authors 2 // This file is part of the aquachain library. 3 // 4 // The aquachain library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The aquachain library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the aquachain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package metrics 18 19 import ( 20 "math" 21 "sort" 22 "sync" 23 "time" 24 ) 25 26 // Initial slice capacity for the values stored in a ResettingTimer 27 const InitialResettingTimerSliceCap = 10 28 29 // ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval. 30 type ResettingTimer interface { 31 Values() []int64 32 Snapshot() ResettingTimer 33 Percentiles([]float64) []int64 34 Mean() float64 35 Time(func()) 36 Update(time.Duration) 37 UpdateSince(time.Time) 38 } 39 40 // GetOrRegisterResettingTimer returns an existing ResettingTimer or constructs and registers a 41 // new StandardResettingTimer. 42 func GetOrRegisterResettingTimer(name string, r Registry) ResettingTimer { 43 if nil == r { 44 r = DefaultRegistry 45 } 46 return r.GetOrRegister(name, NewResettingTimer).(ResettingTimer) 47 } 48 49 // NewRegisteredResettingTimer constructs and registers a new StandardResettingTimer. 50 func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer { 51 c := NewResettingTimer() 52 if nil == r { 53 r = DefaultRegistry 54 } 55 r.Register(name, c) 56 return c 57 } 58 59 // NewResettingTimer constructs a new StandardResettingTimer 60 func NewResettingTimer() ResettingTimer { 61 if !Enabled { 62 return NilResettingTimer{} 63 } 64 return &StandardResettingTimer{ 65 values: make([]int64, 0, InitialResettingTimerSliceCap), 66 } 67 } 68 69 // NilResettingTimer is a no-op ResettingTimer. 70 type NilResettingTimer struct { 71 } 72 73 // Values is a no-op. 74 func (NilResettingTimer) Values() []int64 { return nil } 75 76 // Snapshot is a no-op. 77 func (NilResettingTimer) Snapshot() ResettingTimer { return NilResettingTimer{} } 78 79 // Time is a no-op. 80 func (NilResettingTimer) Time(func()) {} 81 82 // Update is a no-op. 83 func (NilResettingTimer) Update(time.Duration) {} 84 85 // Percentiles panics. 86 func (NilResettingTimer) Percentiles([]float64) []int64 { 87 panic("Percentiles called on a NilResettingTimer") 88 } 89 90 // Mean panics. 91 func (NilResettingTimer) Mean() float64 { 92 panic("Mean called on a NilResettingTimer") 93 } 94 95 // UpdateSince is a no-op. 96 func (NilResettingTimer) UpdateSince(time.Time) {} 97 98 // StandardResettingTimer is the standard implementation of a ResettingTimer. 99 // and Meter. 100 type StandardResettingTimer struct { 101 values []int64 102 mutex sync.Mutex 103 } 104 105 // Values returns a slice with all measurements. 106 func (t *StandardResettingTimer) Values() []int64 { 107 return t.values 108 } 109 110 // Snapshot resets the timer and returns a read-only copy of its contents. 111 func (t *StandardResettingTimer) Snapshot() ResettingTimer { 112 t.mutex.Lock() 113 defer t.mutex.Unlock() 114 currentValues := t.values 115 t.values = make([]int64, 0, InitialResettingTimerSliceCap) 116 117 return &ResettingTimerSnapshot{ 118 values: currentValues, 119 } 120 } 121 122 // Percentiles panics. 123 func (t *StandardResettingTimer) Percentiles([]float64) []int64 { 124 panic("Percentiles called on a StandardResettingTimer") 125 } 126 127 // Mean panics. 128 func (t *StandardResettingTimer) Mean() float64 { 129 panic("Mean called on a StandardResettingTimer") 130 } 131 132 // Record the duration of the execution of the given function. 133 func (t *StandardResettingTimer) Time(f func()) { 134 ts := time.Now() 135 f() 136 t.Update(time.Since(ts)) 137 } 138 139 // Record the duration of an event. 140 func (t *StandardResettingTimer) Update(d time.Duration) { 141 t.mutex.Lock() 142 defer t.mutex.Unlock() 143 t.values = append(t.values, int64(d)) 144 } 145 146 // Record the duration of an event that started at a time and ends now. 147 func (t *StandardResettingTimer) UpdateSince(ts time.Time) { 148 t.mutex.Lock() 149 defer t.mutex.Unlock() 150 t.values = append(t.values, int64(time.Since(ts))) 151 } 152 153 // ResettingTimerSnapshot is a point-in-time copy of another ResettingTimer. 154 type ResettingTimerSnapshot struct { 155 values []int64 156 mean float64 157 thresholdBoundaries []int64 158 calculated bool 159 } 160 161 // Snapshot returns the snapshot. 162 func (t *ResettingTimerSnapshot) Snapshot() ResettingTimer { return t } 163 164 // Time panics. 165 func (*ResettingTimerSnapshot) Time(func()) { 166 panic("Time called on a ResettingTimerSnapshot") 167 } 168 169 // Update panics. 170 func (*ResettingTimerSnapshot) Update(time.Duration) { 171 panic("Update called on a ResettingTimerSnapshot") 172 } 173 174 // UpdateSince panics. 175 func (*ResettingTimerSnapshot) UpdateSince(time.Time) { 176 panic("UpdateSince called on a ResettingTimerSnapshot") 177 } 178 179 // Values returns all values from snapshot. 180 func (t *ResettingTimerSnapshot) Values() []int64 { 181 return t.values 182 } 183 184 // Percentiles returns the boundaries for the input percentiles. 185 func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []int64 { 186 t.calc(percentiles) 187 188 return t.thresholdBoundaries 189 } 190 191 // Mean returns the mean of the snapshotted values 192 func (t *ResettingTimerSnapshot) Mean() float64 { 193 if !t.calculated { 194 t.calc([]float64{}) 195 } 196 197 return t.mean 198 } 199 200 func (t *ResettingTimerSnapshot) calc(percentiles []float64) { 201 sort.Sort(Int64Slice(t.values)) 202 203 count := len(t.values) 204 if count > 0 { 205 min := t.values[0] 206 max := t.values[count-1] 207 208 cumulativeValues := make([]int64, count) 209 cumulativeValues[0] = min 210 for i := 1; i < count; i++ { 211 cumulativeValues[i] = t.values[i] + cumulativeValues[i-1] 212 } 213 214 t.thresholdBoundaries = make([]int64, len(percentiles)) 215 216 thresholdBoundary := max 217 218 for i, pct := range percentiles { 219 if count > 1 { 220 var abs float64 221 if pct >= 0 { 222 abs = pct 223 } else { 224 abs = 100 + pct 225 } 226 // poor man's math.Round(x): 227 // math.Floor(x + 0.5) 228 indexOfPerc := int(math.Floor(((abs / 100.0) * float64(count)) + 0.5)) 229 if pct >= 0 { 230 indexOfPerc -= 1 // index offset=0 231 } 232 thresholdBoundary = t.values[indexOfPerc] 233 } 234 235 t.thresholdBoundaries[i] = thresholdBoundary 236 } 237 238 sum := cumulativeValues[count-1] 239 t.mean = float64(sum) / float64(count) 240 } else { 241 t.thresholdBoundaries = make([]int64, len(percentiles)) 242 t.mean = 0 243 } 244 245 t.calculated = true 246 } 247 248 // Int64Slice attaches the methods of sort.Interface to []int64, sorting in increasing order. 249 type Int64Slice []int64 250 251 func (s Int64Slice) Len() int { return len(s) } 252 func (s Int64Slice) Less(i, j int) bool { return s[i] < s[j] } 253 func (s Int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }