github.com/rudderlabs/rudder-go-kit@v0.30.0/stats/metric/registry.go (about) 1 package metric 2 3 import ( 4 "fmt" 5 "sync" 6 ) 7 8 type ( 9 Tags map[string]string 10 TagsWithValue struct { 11 Tags Tags 12 Value interface{} 13 } 14 ) 15 16 // Registry is a safe way to capture metrics in a highly concurrent environment. 17 // The registry is responsible for creating and storing the various measurements and 18 // guarantees consistency when competing goroutines try to update the same measurement 19 // at the same time. 20 // 21 // E.g. 22 // assuming that you already have created a new registry 23 // 24 // registry := NewRegistry() 25 // 26 // the following is guaranteed to be executed atomically: 27 // 28 // registry.MustGetCounter("key").Inc() 29 type Registry interface { 30 // GetCounter gets a counter by key. If a value for this key 31 // already exists but corresponds to another measurement type, 32 // e.g. a Gauge, an error is returned 33 GetCounter(Measurement) (Counter, error) 34 35 // MustGetCounter gets a counter by key. If a value for this key 36 // already exists but corresponds to another measurement type, 37 // e.g. a Gauge, it panics 38 MustGetCounter(Measurement) Counter 39 40 // GetGauge gets a gauge by key. If a value for this key 41 // already exists but corresponds to another measurement 42 // type, e.g. a Counter, an error is returned 43 GetGauge(Measurement) (Gauge, error) 44 45 // MustGetGauge gets a gauge by key. If a value for this key 46 // already exists but corresponds to another measurement type, 47 // e.g. a Counter, it panics 48 MustGetGauge(Measurement) Gauge 49 50 // GetSimpleMovingAvg gets a moving average by key. If a value for this key 51 // already exists but corresponds to another measurement 52 // type, e.g. a Counter, an error is returned 53 GetSimpleMovingAvg(Measurement) (MovingAverage, error) 54 55 // MustGetSimpleMovingAvg gets a moving average by key. If a value for this key 56 // already exists but corresponds to another measurement type, 57 // e.g. a Counter, it panics 58 MustGetSimpleMovingAvg(Measurement) MovingAverage 59 60 // GetVarMovingAvg gets a moving average by key. If a value for this key 61 // already exists but corresponds to another measurement 62 // type, e.g. a Counter, an error is returned 63 GetVarMovingAvg(m Measurement, age float64) (MovingAverage, error) 64 65 // MustGetVarMovingAvg gets a moving average by key. If a value for this key 66 // already exists but corresponds to another measurement type, 67 // e.g. a Counter, it panics 68 MustGetVarMovingAvg(m Measurement, age float64) MovingAverage 69 70 // Range scans across all metrics 71 Range(f func(key, value interface{}) bool) 72 73 // GetMetricsByName gets all metrics with this name 74 GetMetricsByName(name string) []TagsWithValue 75 } 76 77 // mutexWithMap bundles a lock along with the map it is protecting 78 type mutexWithMap struct { 79 lock *sync.RWMutex 80 value map[Measurement]TagsWithValue 81 } 82 83 func NewRegistry() Registry { 84 counterGenerator := func() interface{} { 85 return NewCounter() 86 } 87 gaugeGenerator := func() interface{} { 88 return NewGauge() 89 } 90 varEwmaGenerator := func() interface{} { 91 return &VariableEWMA{} 92 } 93 simpleEwmaGenerator := func() interface{} { 94 return &SimpleEWMA{} 95 } 96 indexGenerator := func() interface{} { 97 var lock sync.RWMutex 98 v := &mutexWithMap{&lock, map[Measurement]TagsWithValue{}} 99 return v 100 } 101 return ®istry{ 102 counters: sync.Pool{New: counterGenerator}, 103 gauges: sync.Pool{New: gaugeGenerator}, 104 simpleEwmas: sync.Pool{New: simpleEwmaGenerator}, 105 varEwmas: sync.Pool{New: varEwmaGenerator}, 106 sets: sync.Pool{New: indexGenerator}, 107 } 108 } 109 110 type registry struct { 111 store sync.Map 112 nameIndex sync.Map 113 counters sync.Pool 114 gauges sync.Pool 115 simpleEwmas sync.Pool 116 varEwmas sync.Pool 117 sets sync.Pool 118 } 119 120 func (r *registry) GetCounter(m Measurement) (Counter, error) { 121 res := r.get(m, &r.counters) 122 c, ok := res.(Counter) 123 if !ok { 124 return nil, fmt.Errorf("a different type of metric exists in the registry with the same key [%+v]: %T", m, res) 125 } 126 return c, nil 127 } 128 129 func (r *registry) MustGetCounter(m Measurement) Counter { 130 c, err := r.GetCounter(m) 131 if err != nil { 132 panic(err) 133 } 134 return c 135 } 136 137 func (r *registry) GetGauge(m Measurement) (Gauge, error) { 138 res := r.get(m, &r.gauges) 139 g, ok := res.(Gauge) 140 if !ok { 141 return nil, fmt.Errorf("a different type of metric exists in the registry with the same key [%+v]: %T", m, res) 142 } 143 return g, nil 144 } 145 146 func (r *registry) MustGetGauge(m Measurement) Gauge { 147 c, err := r.GetGauge(m) 148 if err != nil { 149 panic(err) 150 } 151 return c 152 } 153 154 func (r *registry) GetSimpleMovingAvg(m Measurement) (MovingAverage, error) { 155 res := r.get(m, &r.simpleEwmas) 156 ma, ok := res.(MovingAverage) 157 if !ok { 158 return nil, fmt.Errorf("a different type of metric exists in the registry with the same key [%+v]: %T", m, res) 159 } 160 return ma, nil 161 } 162 163 func (r *registry) MustGetSimpleMovingAvg(m Measurement) MovingAverage { 164 ma, err := r.GetSimpleMovingAvg(m) 165 if err != nil { 166 panic(err) 167 } 168 return ma 169 } 170 171 func (r *registry) GetVarMovingAvg(m Measurement, age float64) (MovingAverage, error) { 172 decay := 2 / (age + 1) 173 newEwma := r.varEwmas.Get() 174 newEwma.(*VariableEWMA).decay = decay 175 res, ok := r.store.Load(m) 176 if !ok { 177 res, ok = r.store.LoadOrStore(m, newEwma) 178 if ok { 179 r.varEwmas.Put(newEwma) 180 } else { 181 r.updateIndex(m, res) 182 } 183 } 184 ma, ok := res.(*VariableEWMA) 185 if !ok { 186 return nil, fmt.Errorf("a different type of metric exists in the registry with the same key [%+v]: %T", m, res) 187 } 188 if ma.decay != decay { 189 currentAge := 2/ma.decay + 1 190 return nil, fmt.Errorf("another moving average with age %f instead of %f exists in the registry with the same key [%+v]: %T", currentAge, age, m, res) 191 } 192 return ma, nil 193 } 194 195 func (r *registry) MustGetVarMovingAvg(m Measurement, age float64) MovingAverage { 196 ma, err := r.GetVarMovingAvg(m, age) 197 if err != nil { 198 panic(err) 199 } 200 return ma 201 } 202 203 func (r *registry) Range(f func(key, value interface{}) bool) { 204 r.store.Range(f) 205 } 206 207 func (r *registry) GetMetricsByName(name string) []TagsWithValue { 208 metricsSet, ok := r.nameIndex.Load(name) 209 if !ok { 210 return nil 211 } 212 213 var values []TagsWithValue 214 lock := metricsSet.(*mutexWithMap).lock 215 lock.RLock() 216 for _, value := range metricsSet.(*mutexWithMap).value { 217 values = append(values, value) 218 } 219 lock.RUnlock() 220 return values 221 } 222 223 func (r *registry) updateIndex(m Measurement, metric interface{}) { 224 name := m.GetName() 225 newSet := r.sets.Get() 226 res, putBack := r.nameIndex.LoadOrStore(name, newSet) 227 if putBack { 228 r.sets.Put(newSet) 229 } 230 231 lock := res.(*mutexWithMap).lock 232 lock.Lock() 233 res.(*mutexWithMap).value[m] = TagsWithValue{m.GetTags(), metric} 234 lock.Unlock() 235 } 236 237 func (r *registry) get(m Measurement, pool *sync.Pool) interface{} { 238 res, ok := r.store.Load(m) 239 if !ok { 240 newValue := pool.Get() 241 res, ok = r.store.LoadOrStore(m, newValue) 242 if ok { 243 pool.Put(newValue) 244 } else { 245 r.updateIndex(m, res) 246 } 247 } 248 return res 249 }