github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/metrics/aggregation/type.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package aggregation 22 23 import ( 24 "fmt" 25 "strings" 26 27 "github.com/m3db/m3/src/metrics/generated/proto/aggregationpb" 28 "github.com/m3db/m3/src/x/pool" 29 ) 30 31 // Supported aggregation types. 32 const ( 33 UnknownType Type = iota 34 Last 35 Min 36 Max 37 Mean 38 Median 39 Count 40 Sum 41 SumSq 42 Stdev 43 P10 44 P20 45 P30 46 P40 47 P50 48 P60 49 P70 50 P80 51 P90 52 P95 53 P99 54 P999 55 P9999 56 P25 57 P75 58 59 nextTypeID = iota 60 ) 61 62 const ( 63 // maxTypeID is the largest id of all the valid aggregation types. 64 // NB(cw) maxTypeID is guaranteed to be greater or equal 65 // to len(ValidTypes). 66 // Iff ids of all the valid aggregation types are consecutive, 67 // maxTypeID == len(ValidTypes). 68 maxTypeID = nextTypeID - 1 69 70 typesSeparator = "," 71 ) 72 73 var ( 74 emptyStruct struct{} 75 76 // DefaultTypes is a default list of aggregation types. 77 DefaultTypes Types 78 79 // ValidTypes is the list of all the valid aggregation types. 80 ValidTypes = map[Type]struct{}{ 81 Last: emptyStruct, 82 Min: emptyStruct, 83 Max: emptyStruct, 84 Mean: emptyStruct, 85 Median: emptyStruct, 86 Count: emptyStruct, 87 Sum: emptyStruct, 88 SumSq: emptyStruct, 89 Stdev: emptyStruct, 90 P10: emptyStruct, 91 P20: emptyStruct, 92 P25: emptyStruct, 93 P30: emptyStruct, 94 P40: emptyStruct, 95 P50: emptyStruct, 96 P60: emptyStruct, 97 P70: emptyStruct, 98 P75: emptyStruct, 99 P80: emptyStruct, 100 P90: emptyStruct, 101 P95: emptyStruct, 102 P99: emptyStruct, 103 P999: emptyStruct, 104 P9999: emptyStruct, 105 } 106 107 typeStringMap map[string]Type 108 109 typeStringNames = map[Type][]byte{ 110 Last: []byte("last"), 111 Min: []byte("lower"), 112 Max: []byte("upper"), 113 Mean: []byte("mean"), 114 Median: []byte("median"), 115 Count: []byte("count"), 116 Sum: []byte("sum"), 117 SumSq: []byte("sum_sq"), 118 Stdev: []byte("stdev"), 119 P10: []byte("p10"), 120 P20: []byte("p20"), 121 P25: []byte("p25"), 122 P30: []byte("p30"), 123 P40: []byte("p40"), 124 P50: []byte("p50"), 125 P60: []byte("p60"), 126 P70: []byte("p70"), 127 P75: []byte("p75"), 128 P80: []byte("p80"), 129 P90: []byte("p90"), 130 P95: []byte("p95"), 131 P99: []byte("p99"), 132 P999: []byte("p999"), 133 P9999: []byte("p9999"), 134 } 135 136 typeQuantileBytes = map[Type][]byte{ 137 P10: []byte("0.1"), 138 P20: []byte("0.2"), 139 P25: []byte("0.25"), 140 P30: []byte("0.3"), 141 P40: []byte("0.4"), 142 P50: []byte("0.5"), 143 P60: []byte("0.6"), 144 P70: []byte("0.7"), 145 P75: []byte("0.75"), 146 P80: []byte("0.8"), 147 P90: []byte("0.9"), 148 P95: []byte("0.95"), 149 P99: []byte("0.99"), 150 P999: []byte("0.999"), 151 P9999: []byte("0.9999"), 152 } 153 ) 154 155 // Type defines an aggregation function. 156 type Type int 157 158 // NewTypeFromProto creates an aggregation type from a proto. 159 func NewTypeFromProto(input aggregationpb.AggregationType) (Type, error) { 160 aggType := Type(input) 161 if !aggType.IsValid() { 162 return UnknownType, fmt.Errorf("invalid aggregation type from proto: %s", input) 163 } 164 return aggType, nil 165 } 166 167 // ID returns the id of the Type. 168 func (a Type) ID() int { 169 return int(a) 170 } 171 172 // IsValid checks if an Type is valid. 173 func (a Type) IsValid() bool { 174 _, ok := ValidTypes[a] 175 return ok 176 } 177 178 // IsValidForGauge if an Type is valid for Gauge. 179 func (a Type) IsValidForGauge() bool { 180 switch a { 181 case Last, Min, Max, Mean, Count, Sum, SumSq, Stdev: 182 return true 183 default: 184 return false 185 } 186 } 187 188 // IsValidForCounter if an Type is valid for Counter. 189 func (a Type) IsValidForCounter() bool { 190 switch a { 191 case Min, Max, Mean, Count, Sum, SumSq, Stdev: 192 return true 193 default: 194 return false 195 } 196 } 197 198 // IsValidForTimer if an Type is valid for Timer. 199 func (a Type) IsValidForTimer() bool { 200 switch a { 201 case Last: 202 return false 203 default: 204 return true 205 } 206 } 207 208 // Quantile returns the quantile represented by the Type. 209 func (a Type) Quantile() (float64, bool) { 210 switch a { 211 case P10: 212 return 0.1, true 213 case P20: 214 return 0.2, true 215 case P25: 216 return 0.25, true 217 case P30: 218 return 0.3, true 219 case P40: 220 return 0.4, true 221 case P50, Median: 222 return 0.5, true 223 case P60: 224 return 0.6, true 225 case P70: 226 return 0.7, true 227 case P75: 228 return 0.75, true 229 case P80: 230 return 0.8, true 231 case P90: 232 return 0.9, true 233 case P95: 234 return 0.95, true 235 case P99: 236 return 0.99, true 237 case P999: 238 return 0.999, true 239 case P9999: 240 return 0.9999, true 241 default: 242 return 0, false 243 } 244 } 245 246 // QuantileBytes returns the quantile bytes represented by the Type. 247 func (a Type) QuantileBytes() ([]byte, bool) { 248 val, ok := typeQuantileBytes[a] 249 return val, ok 250 } 251 252 // Proto returns the proto of the aggregation type. 253 func (a Type) Proto() (aggregationpb.AggregationType, error) { 254 s := aggregationpb.AggregationType(a) 255 if err := validateProtoType(s); err != nil { 256 return aggregationpb.AggregationType_UNKNOWN, err 257 } 258 return s, nil 259 } 260 261 // MarshalYAML marshals a Type. 262 func (a Type) MarshalYAML() (interface{}, error) { 263 return a.String(), nil 264 } 265 266 // UnmarshalYAML unmarshals text-encoded data into an aggregation type. 267 func (a *Type) UnmarshalYAML(unmarshal func(interface{}) error) error { 268 var str string 269 if err := unmarshal(&str); err != nil { 270 return err 271 } 272 value, err := ParseType(str) 273 if err != nil { 274 return err 275 } 276 *a = value 277 return nil 278 } 279 280 // MarshalText returns the text encoding of an aggregation type. 281 func (a Type) MarshalText() ([]byte, error) { 282 if !a.IsValid() { 283 return nil, fmt.Errorf("invalid aggregation type %s", a.String()) 284 } 285 return []byte(a.String()), nil 286 } 287 288 // UnmarshalText unmarshals text-encoded data into an aggregation type. 289 func (a *Type) UnmarshalText(data []byte) error { 290 str := string(data) 291 parsed, err := ParseType(str) 292 if err != nil { 293 return err 294 } 295 *a = parsed 296 return nil 297 } 298 299 // Name returns the name of the Type. 300 func (a Type) Name() []byte { 301 name, ok := typeStringNames[a] 302 if ok { 303 return name 304 } 305 return a.Bytes() 306 } 307 308 func validateProtoType(a aggregationpb.AggregationType) error { 309 _, ok := aggregationpb.AggregationType_name[int32(a)] 310 if !ok { 311 return fmt.Errorf("invalid proto aggregation type: %v", a) 312 } 313 return nil 314 } 315 316 // ParseType parses an aggregation type. 317 func ParseType(str string) (Type, error) { 318 var ( 319 aggType Type 320 exactMatch bool 321 looseMatch bool 322 ) 323 324 aggType, exactMatch = typeStringMap[str] 325 if !exactMatch { 326 for key, val := range typeStringMap { 327 if strings.ToLower(key) == strings.ToLower(str) { 328 looseMatch = true 329 aggType = val 330 break 331 } 332 } 333 } 334 335 if !exactMatch && !looseMatch { 336 return UnknownType, fmt.Errorf("invalid aggregation type: %s", str) 337 } 338 return aggType, nil 339 } 340 341 // Types is a list of Types. 342 type Types []Type 343 344 // NewTypesFromProto creates a list of aggregation types from a proto. 345 func NewTypesFromProto(input []aggregationpb.AggregationType) (Types, error) { 346 res := make([]Type, len(input)) 347 for i, t := range input { 348 aggType, err := NewTypeFromProto(t) 349 if err != nil { 350 return DefaultTypes, err 351 } 352 res[i] = aggType 353 } 354 return res, nil 355 } 356 357 // Contains checks if the given type is contained in the aggregation types. 358 func (aggTypes Types) Contains(aggType Type) bool { 359 for _, at := range aggTypes { 360 if at == aggType { 361 return true 362 } 363 } 364 return false 365 } 366 367 // IsDefault checks if the Types is the default aggregation type. 368 func (aggTypes Types) IsDefault() bool { 369 return len(aggTypes) == 0 370 } 371 372 // String returns the string representation of the list of aggregation types. 373 func (aggTypes Types) String() string { 374 if len(aggTypes) == 0 { 375 return "" 376 } 377 378 parts := make([]string, len(aggTypes)) 379 for i, aggType := range aggTypes { 380 parts[i] = aggType.String() 381 } 382 return strings.Join(parts, typesSeparator) 383 } 384 385 // IsValidForGauge checks if the list of aggregation types is valid for Gauge. 386 func (aggTypes Types) IsValidForGauge() bool { 387 for _, aggType := range aggTypes { 388 if !aggType.IsValidForGauge() { 389 return false 390 } 391 } 392 return true 393 } 394 395 // IsValidForCounter checks if the list of aggregation types is valid for Counter. 396 func (aggTypes Types) IsValidForCounter() bool { 397 for _, aggType := range aggTypes { 398 if !aggType.IsValidForCounter() { 399 return false 400 } 401 } 402 return true 403 } 404 405 // IsValidForTimer checks if the list of aggregation types is valid for Timer. 406 func (aggTypes Types) IsValidForTimer() bool { 407 for _, aggType := range aggTypes { 408 if !aggType.IsValidForTimer() { 409 return false 410 } 411 } 412 return true 413 } 414 415 // PooledQuantiles returns all the quantiles found in the list 416 // of aggregation types. Using a floats pool if available. 417 // 418 // A boolean will also be returned to indicate whether the 419 // returned float slice is from the pool. 420 func (aggTypes Types) PooledQuantiles(p pool.FloatsPool) ([]float64, bool) { 421 var ( 422 res []float64 423 initialized bool 424 medianAdded bool 425 pooled bool 426 ) 427 for _, aggType := range aggTypes { 428 q, ok := aggType.Quantile() 429 if !ok { 430 continue 431 } 432 // Dedup P50 and Median. 433 if aggType == P50 || aggType == Median { 434 if medianAdded { 435 continue 436 } 437 medianAdded = true 438 } 439 if !initialized { 440 if p == nil { 441 res = make([]float64, 0, len(aggTypes)) 442 } else { 443 res = p.Get(len(aggTypes)) 444 pooled = true 445 } 446 initialized = true 447 } 448 res = append(res, q) 449 } 450 return res, pooled 451 } 452 453 // Proto returns the proto of the aggregation types. 454 func (aggTypes Types) Proto() ([]aggregationpb.AggregationType, error) { 455 // This is the same as returning an empty slice from the functionality perspective. 456 // It makes creating testing fixtures much simpler. 457 if aggTypes == nil { 458 return nil, nil 459 } 460 461 res := make([]aggregationpb.AggregationType, len(aggTypes)) 462 for i, aggType := range aggTypes { 463 s, err := aggType.Proto() 464 if err != nil { 465 return nil, err 466 } 467 res[i] = s 468 } 469 470 return res, nil 471 } 472 473 // ParseTypes parses a list of aggregation types in the form of type1,type2,type3. 474 func ParseTypes(str string) (Types, error) { 475 parts := strings.Split(str, typesSeparator) 476 res := make(Types, len(parts)) 477 for i := range parts { 478 aggType, err := ParseType(parts[i]) 479 if err != nil { 480 return nil, err 481 } 482 res[i] = aggType 483 } 484 return res, nil 485 } 486 487 func init() { 488 typeStringMap = make(map[string]Type, maxTypeID) 489 for aggType := range ValidTypes { 490 typeStringMap[aggType.String()] = aggType 491 } 492 }