• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

m3db / m3metrics / 1174

pending completion
1174

push

travis-ci

web-flow
Incorporate pipeline information in mapping rules and rollup rules (#164)

1651 of 1651 new or added lines in 22 files covered. (100.0%)

6940 of 8128 relevant lines covered (85.38%)

45265.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

47.71
/aggregation/type.go
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
        "strconv"
26
        "strings"
27

28
        "github.com/m3db/m3metrics/generated/proto/aggregationpb"
29
        "github.com/m3db/m3x/pool"
30
)
31

32
// Supported aggregation types.
33
const (
34
        UnknownType Type = iota
35
        Last
36
        Min
37
        Max
38
        Mean
39
        Median
40
        Count
41
        Sum
42
        SumSq
43
        Stdev
44
        P10
45
        P20
46
        P30
47
        P40
48
        P50
49
        P60
50
        P70
51
        P80
52
        P90
53
        P95
54
        P99
55
        P999
56
        P9999
57

58
        nextTypeID = iota
59
)
60

61
const (
62
        // maxTypeID is the largest id of all the valid aggregation types.
63
        // NB(cw) maxTypeID is guaranteed to be greater or equal
64
        // to len(ValidTypes).
65
        // Iff ids of all the valid aggregation types are consecutive,
66
        // maxTypeID == len(ValidTypes).
67
        maxTypeID = nextTypeID - 1
68

69
        typesSeparator = ","
70
)
71

72
var (
73
        emptyStruct struct{}
74

75
        // DefaultTypes is a default list of aggregation types.
76
        DefaultTypes Types
77

78
        // ValidTypes is the list of all the valid aggregation types.
79
        ValidTypes = map[Type]struct{}{
80
                Last:   emptyStruct,
81
                Min:    emptyStruct,
82
                Max:    emptyStruct,
83
                Mean:   emptyStruct,
84
                Median: emptyStruct,
85
                Count:  emptyStruct,
86
                Sum:    emptyStruct,
87
                SumSq:  emptyStruct,
88
                Stdev:  emptyStruct,
89
                P10:    emptyStruct,
90
                P20:    emptyStruct,
91
                P30:    emptyStruct,
92
                P40:    emptyStruct,
93
                P50:    emptyStruct,
94
                P60:    emptyStruct,
95
                P70:    emptyStruct,
96
                P80:    emptyStruct,
97
                P90:    emptyStruct,
98
                P95:    emptyStruct,
99
                P99:    emptyStruct,
100
                P999:   emptyStruct,
101
                P9999:  emptyStruct,
102
        }
103

104
        typeStringMap map[string]Type
105
)
106

107
// Type defines an aggregation function.
108
type Type int
109

110
// NewTypeFromProto creates an aggregation type from a proto.
111
func NewTypeFromProto(input aggregationpb.AggregationType) (Type, error) {
×
112
        aggType := Type(input)
×
113
        if !aggType.IsValid() {
×
114
                return UnknownType, fmt.Errorf("invalid aggregation type from proto: %s", input)
×
115
        }
×
116
        return aggType, nil
×
117
}
118

119
// ID returns the id of the Type.
120
func (a Type) ID() int {
5,398✔
121
        return int(a)
5,398✔
122
}
5,398✔
123

124
// IsValid checks if an Type is valid.
125
func (a Type) IsValid() bool {
196✔
126
        _, ok := ValidTypes[a]
196✔
127
        return ok
196✔
128
}
196✔
129

130
// IsValidForGauge if an Type is valid for Gauge.
131
func (a Type) IsValidForGauge() bool {
×
132
        switch a {
×
133
        case Last, Min, Max, Mean, Count, Sum, SumSq, Stdev:
×
134
                return true
×
135
        default:
×
136
                return false
×
137
        }
138
}
139

140
// IsValidForCounter if an Type is valid for Counter.
141
func (a Type) IsValidForCounter() bool {
×
142
        switch a {
×
143
        case Min, Max, Mean, Count, Sum, SumSq, Stdev:
×
144
                return true
×
145
        default:
×
146
                return false
×
147
        }
148
}
149

150
// IsValidForTimer if an Type is valid for Timer.
151
func (a Type) IsValidForTimer() bool {
×
152
        switch a {
×
153
        case Last:
×
154
                return false
×
155
        default:
×
156
                return true
×
157
        }
158
}
159

160
// Quantile returns the quantile represented by the Type.
161
func (a Type) Quantile() (float64, bool) {
2,796✔
162
        switch a {
2,796✔
163
        case P10:
160✔
164
                return 0.1, true
160✔
165
        case P20:
160✔
166
                return 0.2, true
160✔
167
        case P30:
160✔
168
                return 0.3, true
160✔
169
        case P40:
160✔
170
                return 0.4, true
160✔
171
        case P50, Median:
292✔
172
                return 0.5, true
292✔
173
        case P60:
160✔
174
                return 0.6, true
160✔
175
        case P70:
160✔
176
                return 0.7, true
160✔
177
        case P80:
160✔
178
                return 0.8, true
160✔
179
        case P90:
162✔
180
                return 0.9, true
162✔
181
        case P95:
226✔
182
                return 0.95, true
226✔
183
        case P99:
232✔
184
                return 0.99, true
232✔
185
        case P999:
160✔
186
                return 0.999, true
160✔
187
        case P9999:
166✔
188
                return 0.9999, true
166✔
189
        default:
438✔
190
                return 0, false
438✔
191
        }
192
}
193

194
// Proto returns the proto of the aggregation type.
195
func (a Type) Proto() (aggregationpb.AggregationType, error) {
×
196
        s := aggregationpb.AggregationType(a)
×
197
        if err := validateProtoType(s); err != nil {
×
198
                return aggregationpb.AggregationType_UNKNOWN, err
×
199
        }
×
200
        return s, nil
×
201
}
202

203
// MarshalJSON returns the JSON encoding of an aggregation type.
204
func (a Type) MarshalJSON() ([]byte, error) {
×
205
        if !a.IsValid() {
×
206
                return nil, fmt.Errorf("invalid aggregation type %s", a.String())
×
207
        }
×
208
        marshalled := strconv.Quote(a.String())
×
209
        return []byte(marshalled), nil
×
210
}
211

212
// UnmarshalJSON unmarshals JSON-encoded data into an aggregation type.
213
func (a *Type) UnmarshalJSON(data []byte) error {
×
214
        str := string(data)
×
215
        unquoted, err := strconv.Unquote(str)
×
216
        if err != nil {
×
217
                return err
×
218
        }
×
219
        parsed, err := ParseType(unquoted)
×
220
        if err != nil {
×
221
                return err
×
222
        }
×
223
        *a = parsed
×
224
        return nil
×
225
}
226

227
// UnmarshalYAML unmarshals aggregation type from a string.
228
func (a *Type) UnmarshalYAML(unmarshal func(interface{}) error) error {
18✔
229
        var str string
18✔
230
        if err := unmarshal(&str); err != nil {
18✔
231
                return err
×
232
        }
×
233

234
        parsed, err := ParseType(str)
18✔
235
        if err != nil {
22✔
236
                return err
4✔
237
        }
4✔
238
        *a = parsed
14✔
239
        return nil
14✔
240
}
241

242
func validateProtoType(a aggregationpb.AggregationType) error {
×
243
        _, ok := aggregationpb.AggregationType_name[int32(a)]
×
244
        if !ok {
×
245
                return fmt.Errorf("invalid proto aggregation type: %v", a)
×
246
        }
×
247
        return nil
×
248
}
249

250
// ParseType parses an aggregation type.
251
func ParseType(str string) (Type, error) {
82✔
252
        aggType, ok := typeStringMap[str]
82✔
253
        if !ok {
92✔
254
                return UnknownType, fmt.Errorf("invalid aggregation type: %s", str)
10✔
255
        }
10✔
256
        return aggType, nil
72✔
257
}
258

259
// Types is a list of Types.
260
type Types []Type
261

262
// NewTypesFromProto creates a list of aggregation types from a proto.
263
func NewTypesFromProto(input []aggregationpb.AggregationType) (Types, error) {
×
264
        res := make([]Type, len(input))
×
265
        for i, t := range input {
×
266
                aggType, err := NewTypeFromProto(t)
×
267
                if err != nil {
×
268
                        return DefaultTypes, err
×
269
                }
×
270
                res[i] = aggType
×
271
        }
272
        return res, nil
×
273
}
274

275
// UnmarshalYAML unmarshals aggregation types from a string.
276
func (aggTypes *Types) UnmarshalYAML(unmarshal func(interface{}) error) error {
22✔
277
        var str string
22✔
278
        if err := unmarshal(&str); err != nil {
22✔
279
                return err
×
280
        }
×
281

282
        parsed, err := ParseTypes(str)
22✔
283
        if err != nil {
28✔
284
                return err
6✔
285
        }
6✔
286
        *aggTypes = parsed
16✔
287
        return nil
16✔
288
}
289

290
// Contains checks if the given type is contained in the aggregation types.
291
func (aggTypes Types) Contains(aggType Type) bool {
×
292
        for _, at := range aggTypes {
×
293
                if at == aggType {
×
294
                        return true
×
295
                }
×
296
        }
297
        return false
×
298
}
299

300
// IsDefault checks if the Types is the default aggregation type.
301
func (aggTypes Types) IsDefault() bool {
4✔
302
        return len(aggTypes) == 0
4✔
303
}
4✔
304

305
// String returns the string representation of the list of aggregation types.
306
func (aggTypes Types) String() string {
×
307
        if len(aggTypes) == 0 {
×
308
                return ""
×
309
        }
×
310

311
        parts := make([]string, len(aggTypes))
×
312
        for i, aggType := range aggTypes {
×
313
                parts[i] = aggType.String()
×
314
        }
×
315
        return strings.Join(parts, typesSeparator)
×
316
}
317

318
// IsValidForGauge checks if the list of aggregation types is valid for Gauge.
319
func (aggTypes Types) IsValidForGauge() bool {
×
320
        for _, aggType := range aggTypes {
×
321
                if !aggType.IsValidForGauge() {
×
322
                        return false
×
323
                }
×
324
        }
325
        return true
×
326
}
327

328
// IsValidForCounter checks if the list of aggregation types is valid for Counter.
329
func (aggTypes Types) IsValidForCounter() bool {
×
330
        for _, aggType := range aggTypes {
×
331
                if !aggType.IsValidForCounter() {
×
332
                        return false
×
333
                }
×
334
        }
335
        return true
×
336
}
337

338
// IsValidForTimer checks if the list of aggregation types is valid for Timer.
339
func (aggTypes Types) IsValidForTimer() bool {
×
340
        for _, aggType := range aggTypes {
×
341
                if !aggType.IsValidForTimer() {
×
342
                        return false
×
343
                }
×
344
        }
345
        return true
×
346
}
347

348
// PooledQuantiles returns all the quantiles found in the list
349
// of aggregation types. Using a floats pool if available.
350
//
351
// A boolean will also be returned to indicate whether the
352
// returned float slice is from the pool.
353
func (aggTypes Types) PooledQuantiles(p pool.FloatsPool) ([]float64, bool) {
86✔
354
        var (
86✔
355
                res         []float64
86✔
356
                initialized bool
86✔
357
                medianAdded bool
86✔
358
                pooled      bool
86✔
359
        )
86✔
360
        for _, aggType := range aggTypes {
784✔
361
                q, ok := aggType.Quantile()
698✔
362
                if !ok {
1,108✔
363
                        continue
410✔
364
                }
365
                // Dedup P50 and Median.
366
                if aggType == P50 || aggType == Median {
414✔
367
                        if medianAdded {
184✔
368
                                continue
58✔
369
                        }
370
                        medianAdded = true
68✔
371
                }
372
                if !initialized {
302✔
373
                        if p == nil {
78✔
374
                                res = make([]float64, 0, len(aggTypes))
6✔
375
                        } else {
72✔
376
                                res = p.Get(len(aggTypes))
66✔
377
                                pooled = true
66✔
378
                        }
66✔
379
                        initialized = true
72✔
380
                }
381
                res = append(res, q)
230✔
382
        }
383
        return res, pooled
86✔
384
}
385

386
// Proto returns the proto of the aggregation types.
387
func (aggTypes Types) Proto() ([]aggregationpb.AggregationType, error) {
×
388
        // This is the same as returning an empty slice from the functionality perspective.
×
389
        // It makes creating testing fixtures much simpler.
×
390
        if aggTypes == nil {
×
391
                return nil, nil
×
392
        }
×
393

394
        res := make([]aggregationpb.AggregationType, len(aggTypes))
×
395
        for i, aggType := range aggTypes {
×
396
                s, err := aggType.Proto()
×
397
                if err != nil {
×
398
                        return nil, err
×
399
                }
×
400
                res[i] = s
×
401
        }
402

403
        return res, nil
×
404
}
405

406
// ParseTypes parses a list of aggregation types in the form of type1,type2,type3.
407
func ParseTypes(str string) (Types, error) {
26✔
408
        parts := strings.Split(str, typesSeparator)
26✔
409
        res := make(Types, len(parts))
26✔
410
        for i := range parts {
90✔
411
                aggType, err := ParseType(parts[i])
64✔
412
                if err != nil {
70✔
413
                        return nil, err
6✔
414
                }
6✔
415
                res[i] = aggType
58✔
416
        }
417
        return res, nil
20✔
418
}
419

420
func init() {
2✔
421
        typeStringMap = make(map[string]Type, maxTypeID)
2✔
422
        for aggType := range ValidTypes {
46✔
423
                typeStringMap[aggType.String()] = aggType
44✔
424
        }
44✔
425
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc