Skip to content

Commit a418fbd

Browse files
Fix asPercent error when series has more values than totalSeries (#87)
* Check if totalSeries value length is less than series value length * Set NaN for series values after totalSeries values length * Add test for differing values length and step for series and totalSeries * Add handling for length of seriesList < length of totalSeries values * Add docstring for getPercentages
1 parent 1cf8078 commit a418fbd

File tree

2 files changed

+64
-27
lines changed

2 files changed

+64
-27
lines changed

expr/functions/asPercent/function.go

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,46 @@ func sumSeries(seriesList []*types.MetricData) *types.MetricData {
6161
return result
6262
}
6363

64+
func calculatePercentage(seriesValue, totalValue float64) float64 {
65+
var value float64
66+
if math.IsNaN(seriesValue) || math.IsNaN(totalValue) || totalValue == 0 {
67+
value = math.NaN()
68+
} else {
69+
value = seriesValue * (100 / totalValue)
70+
}
71+
return value
72+
}
73+
74+
// GetPercentages contains the logic to apply to the series in order to properly
75+
// calculate percentages. If the length of the values in series and totalSeries are
76+
// not equal, special handling is required. If the number of values in seriesList is
77+
// greater than the number of values in totalSeries, math.NaN() needs to be set to the
78+
// indices in series starting at the length of totalSeries.Values. If the number of values
79+
// in totalSeries is greater than the number of values in series, then math.NaN() needs
80+
// to be appended to series until its values have the same length as totalSeries.Values
81+
func getPercentages(series, totalSeries *types.MetricData) {
82+
// If there are more series values than totalSeries values, set series value to math.NaN() for those indices
83+
if len(series.Values) > len(totalSeries.Values) {
84+
for i := 0; i < len(totalSeries.Values); i++ {
85+
series.Values[i] = calculatePercentage(series.Values[i], totalSeries.Values[i])
86+
}
87+
for i := len(totalSeries.Values); i < len(series.Values); i++ {
88+
series.Values[i] = math.NaN()
89+
}
90+
} else {
91+
for i := range series.Values {
92+
series.Values[i] = calculatePercentage(series.Values[i], totalSeries.Values[i])
93+
}
94+
95+
// If there are more totalSeries values than series values, append math.NaN() to the series values
96+
if lengthDiff := len(totalSeries.Values) - len(series.Values); lengthDiff > 0 {
97+
for i := 0; i < lengthDiff; i++ {
98+
series.Values = append(series.Values, math.NaN())
99+
}
100+
}
101+
}
102+
}
103+
64104
func groupByNodes(seriesList []*types.MetricData, nodesOrTags []parser.NodeOrTag) map[string][]*types.MetricData {
65105
groups := make(map[string][]*types.MetricData)
66106

@@ -85,14 +125,7 @@ func seriesAsPercent(arg, total []*types.MetricData) []*types.MetricData {
85125
} else if len(total) == 1 {
86126
// asPercent(seriesList, totalSeries)
87127
for _, a := range arg {
88-
for i := range a.Values {
89-
t := total[0].Values[i]
90-
if math.IsNaN(a.Values[i]) || math.IsNaN(t) || t == 0 {
91-
a.Values[i] = math.NaN()
92-
} else {
93-
a.Values[i] *= 100 / t
94-
}
95-
}
128+
getPercentages(a, total[0])
96129

97130
a.Name = "asPercent(" + a.Name + "," + total[0].Name + ")"
98131
}
@@ -103,14 +136,7 @@ func seriesAsPercent(arg, total []*types.MetricData) []*types.MetricData {
103136
if len(arg) <= len(total) {
104137
// asPercent(seriesList, totalSeriesList) for series with len(seriesList) <= len(totalSeriesList)
105138
for n, a := range arg {
106-
for i := range a.Values {
107-
t := total[n].Values[i]
108-
if math.IsNaN(a.Values[i]) || math.IsNaN(t) || t == 0 {
109-
a.Values[i] = math.NaN()
110-
} else {
111-
a.Values[i] *= 100 / t
112-
}
113-
}
139+
getPercentages(a, total[n])
114140

115141
a.Name = "asPercent(" + a.Name + "," + total[n].Name + ")"
116142
}
@@ -215,14 +241,8 @@ func seriesGroup2AsPercent(arg, total []*types.MetricData, nodesOrTags []parser.
215241
// asPercent(seriesList, totalSeries, *nodes)
216242
start := len(arg)
217243
for _, a := range argGroup {
218-
for i := range a.Values {
219-
t := totalGroup[0].Values[i]
220-
if math.IsNaN(a.Values[i]) || math.IsNaN(t) || t == 0 {
221-
a.Values[i] = math.NaN()
222-
} else {
223-
a.Values[i] *= 100 / t
224-
}
225-
}
244+
getPercentages(a, totalGroup[0])
245+
226246
a.Name = "asPercent(" + a.Name + "," + totalGroup[0].Name + ")"
227247
arg = append(arg, a)
228248
}

expr/functions/asPercent/function_test.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func init() {
2323
}
2424
}
2525

26-
func TestAsPersent(t *testing.T) {
26+
func TestAsPercent(t *testing.T) {
2727
now32 := int64(time.Now().Unix())
2828
NaN := math.NaN()
2929

@@ -71,6 +71,23 @@ func TestAsPersent(t *testing.T) {
7171
types.MakeMetricData("asPercent(Server2.memory.used,Server3.memory.total)", []float64{25, 62.5, 1000}, 1, now32),
7272
},
7373
},
74+
{
75+
"asPercent(metricC*,metricD*)", // This test is to verify that passing in metrics with different number of values and different step values for the series and the totalSeries does not throw an error
76+
map[parser.MetricRequest][]*types.MetricData{
77+
{"metricC*", 0, 1}: {
78+
types.MakeMetricData("metricC1", []float64{1, 20, 10, 15, 5}, 1, now32), // Test that error isn't thrown when seriesList has more values than totalSeries
79+
types.MakeMetricData("metricC2", []float64{1, 10, 20, 15, 5}, 1, now32),
80+
},
81+
{"metricD*", 0, 1}: {
82+
types.MakeMetricData("metricD1", []float64{4, 4, 8}, 2, now32),
83+
types.MakeMetricData("metricD2", []float64{4, 16, 2}, 2, now32),
84+
},
85+
},
86+
[]*types.MetricData{types.MakeMetricData("asPercent(metricC1,metricD1)",
87+
[]float64{25, 500, 125, math.NaN(), math.NaN(), math.NaN()}, 1, now32),
88+
types.MakeMetricData("asPercent(metricC2,metricD2)",
89+
[]float64{25, 62.5, 1000, math.NaN(), math.NaN(), math.NaN()}, 1, now32)},
90+
},
7491

7592
// Extend tests
7693
{
@@ -151,7 +168,7 @@ func TestAsPersent(t *testing.T) {
151168
}
152169
}
153170

154-
func TestAsPersentAligment(t *testing.T) {
171+
func TestAsPercentAlignment(t *testing.T) {
155172
now32 := int64(time.Now().Unix())
156173
NaN := math.NaN()
157174
testAlignments := []th.EvalTestItem{
@@ -211,7 +228,7 @@ func TestAsPersentAligment(t *testing.T) {
211228
}
212229
}
213230

214-
func TestAsPersentGroup(t *testing.T) {
231+
func TestAsPercentGroup(t *testing.T) {
215232
now32 := int64(time.Now().Unix())
216233
NaN := math.NaN()
217234

0 commit comments

Comments
 (0)