github.com/haraldrudell/parl@v0.4.176/counter/counters.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package counter 7 8 import ( 9 "sync" 10 "time" 11 12 "github.com/haraldrudell/parl" 13 "github.com/haraldrudell/parl/perrors" 14 "golang.org/x/exp/maps" 15 "golang.org/x/exp/slices" 16 ) 17 18 // Counters is a container for counters, rate-counters and datapoints. Thread-Safe. 19 // - a counter is Inc-Dec with: value running max 20 // - a rate-counter is a counter with addtional measuring over short time periods: 21 // - — value: rate of increase: current/max/average 22 // - — running: rate up or down, max increase/decrease rate, 23 type Counters struct { 24 lock sync.RWMutex 25 ordered []parl.CounterID // behind lock: ordered list of counters and datapoints 26 m map[parl.CounterID]any // behind lock 27 RateRunner 28 } 29 30 var _ parl.CounterSet = &Counters{} // Counters is a parl.CounterSet 31 var _ parl.CounterSetData = &Counters{} // Counters is a parl.CounterSet 32 var _ parl.CounterStore = &Counters{} // Counters is a parl.CounterStore 33 34 func newCounters(g0 parl.GoGen) (counters parl.Counters) { 35 return &Counters{ 36 m: map[parl.CounterID]any{}, 37 RateRunner: *NewRateRunner(g0), 38 } 39 } 40 41 func (cs *Counters) GetOrCreateCounter(name parl.CounterID, period ...time.Duration) (counter parl.Counter) { 42 counter = cs.getOrCreate(false, name, period...).(parl.Counter) 43 return 44 } 45 46 func (cs *Counters) GetCounter(name parl.CounterID) (counter parl.Counter) { 47 counter = cs.getOrCreate(false, name).(parl.Counter) 48 return 49 } 50 51 func (cs *Counters) GetOrCreateDatapoint(name parl.CounterID, period time.Duration) (datapoint parl.Datapoint) { 52 datapoint = cs.getOrCreate(true, name, period).(parl.Datapoint) 53 return 54 } 55 56 func (cs *Counters) GetCounters() (list []parl.CounterID, m map[parl.CounterID]any) { 57 cs.lock.RLock() 58 defer cs.lock.RUnlock() 59 60 list = slices.Clone(cs.ordered) 61 m = maps.Clone(cs.m) 62 63 return 64 } 65 66 func (cs *Counters) ResetCounters(stopRateCounters bool) { 67 cs.lock.Lock() 68 defer cs.lock.Unlock() 69 70 _, m := cs.GetCounters() 71 for _, item := range m { 72 if counter, ok := item.(parl.CounterValues); ok { 73 counter.GetReset() 74 } else if datapoint, ok := item.(parl.DatapointValue); ok { 75 datapoint.CloneDatapointReset() 76 } else { 77 panic(perrors.ErrorfPF("Bad item in map: %T", item)) 78 } 79 } 80 } 81 82 func (cs *Counters) Exists(name parl.CounterID) (exists bool) { 83 exists = cs.GetNamedCounter(name) != nil 84 return 85 } 86 87 func (cs *Counters) Value(name parl.CounterID) (value uint64) { 88 if counter, ok := cs.GetNamedCounter(name).(interface{ Value() (value uint64) }); ok { 89 value = counter.Value() 90 } 91 return 92 } 93 94 func (cs *Counters) Get(name parl.CounterID) (value, running, max uint64) { 95 if counter, ok := cs.GetNamedCounter(name).(interface { 96 Get() (value, running, max uint64) 97 }); ok { 98 value, running, max = counter.Get() 99 } 100 return 101 } 102 103 func (cs *Counters) Rates(name parl.CounterID) (rates map[parl.RateType]float64) { 104 if counter, ok := cs.GetNamedCounter(name).(interface { 105 Rates() (rates map[parl.RateType]float64) 106 }); ok { 107 rates = counter.Rates() 108 } 109 return 110 } 111 112 func (cs *Counters) DatapointValue(name parl.CounterID) (datapointValue uint64) { 113 if counter, ok := cs.GetNamedCounter(name).(interface { 114 DatapointValue() (datapointValue uint64) 115 }); ok { 116 datapointValue = counter.DatapointValue() 117 } 118 return 119 } 120 121 func (cs *Counters) DatapointMax(name parl.CounterID) (datapointMax uint64) { 122 if counter, ok := cs.GetNamedCounter(name).(interface { 123 DatapointMax() (datapointMax uint64) 124 }); ok { 125 datapointMax = counter.DatapointMax() 126 } 127 return 128 } 129 130 func (cs *Counters) DatapointMin(name parl.CounterID) (datapointMin uint64) { 131 if counter, ok := cs.GetNamedCounter(name).(interface { 132 DatapointMin() (datapointMin uint64) 133 }); ok { 134 datapointMin = counter.DatapointMin() 135 } 136 return 137 } 138 139 func (cs *Counters) GetDatapoint(name parl.CounterID) (value, max, min uint64, isValid bool, average float64, n uint64) { 140 if counter, ok := cs.GetNamedCounter(name).(interface { 141 GetDatapoint() (value, max, min uint64, isValid bool, average float64, n uint64) 142 }); ok { 143 value, max, min, isValid, average, n = counter.GetDatapoint() 144 } 145 return 146 } 147 148 func (cs *Counters) GetNamedCounter(name parl.CounterID) (counter any) { 149 cs.lock.RLock() 150 defer cs.lock.RUnlock() 151 152 counter = cs.m[name] 153 return 154 } 155 156 func (cs *Counters) getOrCreate(isDatapoint bool, name parl.CounterID, period ...time.Duration) (item any) { 157 cs.lock.Lock() 158 defer cs.lock.Unlock() 159 160 // check for existing counter or datapoint 161 var ok bool 162 if item, ok = cs.m[name]; ok { 163 return // counter exists return 164 } 165 166 // instantiate counter or datapoint 167 var period0 time.Duration 168 if len(period) > 0 { 169 period0 = period[0] 170 } 171 if !isDatapoint { 172 if period0 == 0 { 173 item = newCounter() // non-rate counter 174 } else { 175 var r = newRateCounter() 176 item = r 177 cs.AddTask(period0, r) 178 } 179 } else { 180 item = newDatapoint(period0) 181 } 182 183 // store the new counter or datapoint 184 cs.ordered = append(cs.ordered, name) 185 cs.m[name] = item 186 187 return 188 }