github.com/go-graphite/carbonapi@v0.17.0/expr/functions/asPercent/function_test.go (about) 1 package asPercent 2 3 import ( 4 "context" 5 "math" 6 "testing" 7 "time" 8 9 "github.com/go-graphite/carbonapi/expr/interfaces" 10 "github.com/go-graphite/carbonapi/expr/metadata" 11 "github.com/go-graphite/carbonapi/expr/types" 12 "github.com/go-graphite/carbonapi/pkg/parser" 13 th "github.com/go-graphite/carbonapi/tests" 14 ) 15 16 var ( 17 md []interfaces.FunctionMetadata = New("") 18 ) 19 20 func init() { 21 for _, m := range md { 22 metadata.RegisterFunction(m.Name, m.F) 23 } 24 } 25 26 func TestAsPercent(t *testing.T) { 27 now32 := int64(time.Now().Unix()) 28 NaN := math.NaN() 29 30 tests := []th.EvalTestItem{ 31 { 32 "asPercent(metric1,metric2)", 33 map[parser.MetricRequest][]*types.MetricData{ 34 {Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, NaN, NaN, 3, 4, 12}, 1, now32)}, 35 {Metric: "metric2", From: 0, Until: 1}: {types.MakeMetricData("metric2", []float64{2, NaN, 3, NaN, 0, 6}, 1, now32)}, 36 }, 37 []*types.MetricData{types.MakeMetricData("asPercent(metric1,metric2)", 38 []float64{50, NaN, NaN, NaN, NaN, 200}, 1, now32)}, 39 }, 40 { 41 "asPercent(metric1,'5')", // Ensure quoted numeric values are handled 42 map[parser.MetricRequest][]*types.MetricData{ 43 {Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, NaN, NaN, 3, 4, 12}, 1, now32)}, 44 }, 45 []*types.MetricData{types.MakeMetricData("asPercent(metric1,5)", 46 []float64{20, NaN, NaN, 60, 80, 240}, 1, now32)}, 47 }, 48 { 49 "asPercent(metricA*,metricB*)", 50 map[parser.MetricRequest][]*types.MetricData{ 51 {Metric: "metricA*", From: 0, Until: 1}: { 52 types.MakeMetricData("metricA1", []float64{1, 20, 10}, 1, now32), 53 types.MakeMetricData("metricA2", []float64{1, 10, 20}, 1, now32), 54 }, 55 {Metric: "metricB*", From: 0, Until: 1}: { 56 types.MakeMetricData("metricB1", []float64{4, 4, 8}, 1, now32), 57 types.MakeMetricData("metricB2", []float64{4, 16, 2}, 1, now32), 58 }, 59 }, 60 []*types.MetricData{types.MakeMetricData("asPercent(metricA1,metricB1)", 61 []float64{25, 500, 125}, 1, now32), 62 types.MakeMetricData("asPercent(metricA2,metricB2)", 63 []float64{25, 62.5, 1000}, 1, now32)}, 64 }, 65 { 66 "asPercent(Server{1,2}.memory.used,Server{1,3}.memory.total)", 67 map[parser.MetricRequest][]*types.MetricData{ 68 {Metric: "Server{1,2}.memory.used", From: 0, Until: 1}: { 69 types.MakeMetricData("Server1.memory.used", []float64{1, 20, 10}, 1, now32), 70 types.MakeMetricData("Server2.memory.used", []float64{1, 10, 20}, 1, now32), 71 }, 72 {Metric: "Server{1,3}.memory.total", From: 0, Until: 1}: { 73 types.MakeMetricData("Server1.memory.total", []float64{4, 4, 8}, 1, now32), 74 types.MakeMetricData("Server3.memory.total", []float64{4, 16, 2}, 1, now32), 75 }, 76 }, 77 []*types.MetricData{ 78 types.MakeMetricData("asPercent(Server1.memory.used,Server1.memory.total)", []float64{25, 500, 125}, 1, now32), 79 types.MakeMetricData("asPercent(Server2.memory.used,Server3.memory.total)", []float64{25, 62.5, 1000}, 1, now32), 80 }, 81 }, 82 { 83 "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 84 map[parser.MetricRequest][]*types.MetricData{ 85 {Metric: "metricC*", From: 0, Until: 1}: { 86 types.MakeMetricData("metricC1", []float64{1, 20, 10, 15, 5}, 1, now32), // Test that error isn't thrown when seriesList has more values than totalSeries 87 types.MakeMetricData("metricC2", []float64{1, 10, 20, 15, 5}, 1, now32), 88 }, 89 {Metric: "metricD*", From: 0, Until: 1}: { 90 types.MakeMetricData("metricD1", []float64{4, 4, 8}, 2, now32), 91 types.MakeMetricData("metricD2", []float64{4, 16, 2}, 2, now32), 92 }, 93 }, 94 []*types.MetricData{types.MakeMetricData("asPercent(metricC1,metricD1)", 95 []float64{25, 500, 125, math.NaN(), math.NaN(), math.NaN()}, 1, now32), 96 types.MakeMetricData("asPercent(metricC2,metricD2)", 97 []float64{25, 62.5, 1000, math.NaN(), math.NaN(), math.NaN()}, 1, now32)}, 98 }, 99 { 100 "asPercent(metricE*,metricF*)", // This test is to verify that passing in metrics with different lengths and different number of values and different step values for the series and the totalSeries does not throw an error 101 map[parser.MetricRequest][]*types.MetricData{ 102 {Metric: "metricE*", From: 0, Until: 1}: { 103 types.MakeMetricData("metricE1", []float64{1, 20, 10, 15, 5}, 1, now32), // Test that error isn't thrown when seriesList has more values than totalSeries 104 types.MakeMetricData("metricE2", []float64{1, 10, 20, 15, 5}, 1, now32), 105 types.MakeMetricData("metricE3", []float64{1, 10, 20, 15, 5}, 1, now32), 106 }, 107 {Metric: "metricF*", From: 0, Until: 1}: { 108 types.MakeMetricData("metricF1", []float64{4, 4, 8}, 2, now32), 109 types.MakeMetricData("metricF2", []float64{4, 16, 2}, 2, now32), 110 }, 111 }, 112 []*types.MetricData{types.MakeMetricData("asPercent(metricE1,metricF1)", 113 []float64{25, 500, 125, math.NaN(), math.NaN(), math.NaN()}, 1, now32), 114 types.MakeMetricData("asPercent(metricE2,metricF2)", 115 []float64{25, 62.5, 1000, math.NaN(), math.NaN(), math.NaN()}, 1, now32), 116 types.MakeMetricData("asPercent(metricE3,MISSING)", 117 []float64{math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN(), math.NaN()}, 1, now32)}, 118 }, 119 120 // Extend tests 121 { 122 "asPercent(metric*)", 123 map[parser.MetricRequest][]*types.MetricData{ 124 {Metric: "metric*", From: 0, Until: 1}: { 125 types.MakeMetricData("metric1", []float64{1, NaN, NaN, 3, 4, 14}, 1, now32), 126 types.MakeMetricData("metric2", []float64{4, NaN, 3, NaN, 0, 6}, 1, now32), 127 }, 128 }, 129 []*types.MetricData{ 130 types.MakeMetricData("asPercent(metric1)", []float64{20, NaN, NaN, 100, 100, 70}, 1, now32), 131 types.MakeMetricData("asPercent(metric2)", []float64{80, NaN, 100, NaN, 0, 30}, 1, now32), 132 }, 133 }, 134 { 135 "asPercent(Server{1,2}.memory.used,Server{1,2}.memory.total)", 136 map[parser.MetricRequest][]*types.MetricData{ 137 {Metric: "Server{1,2}.memory.used", From: 0, Until: 1}: { 138 types.MakeMetricData("Server1.memory.used", []float64{1, 20, 10}, 1, now32), 139 types.MakeMetricData("Server2.memory.used", []float64{1, 11, 20}, 1, now32), 140 }, 141 {Metric: "Server{1,2}.memory.total", From: 0, Until: 1}: { 142 types.MakeMetricData("Server2.memory.total", []float64{4, 2, 2}, 1, now32), 143 types.MakeMetricData("Server1.memory.total", []float64{4, 4, 8}, 1, now32), 144 }, 145 }, 146 []*types.MetricData{ 147 types.MakeMetricData("asPercent(Server1.memory.used,Server1.memory.total)", []float64{25, 500, 125}, 1, now32), 148 types.MakeMetricData("asPercent(Server2.memory.used,Server2.memory.total)", []float64{25, 550, 1000}, 1, now32), 149 }, 150 }, 151 { 152 "asPercent(Server{1,2}.memory.used,Server{1,2,3}.memory.total)", 153 map[parser.MetricRequest][]*types.MetricData{ 154 {Metric: "Server{1,2}.memory.used", From: 0, Until: 1}: { 155 types.MakeMetricData("Server1.memory.used", []float64{1, 20, 15}, 1, now32), 156 types.MakeMetricData("Server2.memory.used", []float64{1, 11, 20}, 1, now32), 157 }, 158 {Metric: "Server{1,2,3}.memory.total", From: 0, Until: 1}: { 159 types.MakeMetricData("Server1.memory.total", []float64{4, 40, 25}, 1, now32), 160 types.MakeMetricData("Server2.memory.total", []float64{4, 20, 40}, 1, now32), 161 types.MakeMetricData("Server3.memory.total", []float64{4, 20, 40}, 1, now32), 162 }, 163 }, 164 []*types.MetricData{ 165 types.MakeMetricData("asPercent(Server1.memory.used,Server1.memory.total)", []float64{25, 50, 60}, 1, now32), 166 types.MakeMetricData("asPercent(Server2.memory.used,Server2.memory.total)", []float64{25, 55, 50}, 1, now32), 167 types.MakeMetricData("asPercent(MISSING,Server3.memory.total)", []float64{NaN, NaN, NaN}, 1, now32), 168 }, 169 }, 170 { 171 "asPercent(Server{1,2,3}.memory.used,Server{1,2}.memory.total)", 172 map[parser.MetricRequest][]*types.MetricData{ 173 {Metric: "Server{1,2,3}.memory.used", From: 0, Until: 1}: { 174 types.MakeMetricData("Server1.memory.used", []float64{1, 20, 15}, 1, now32), 175 types.MakeMetricData("Server2.memory.used", []float64{1, 11, 20}, 1, now32), 176 types.MakeMetricData("Server3.memory.used", []float64{1, 11, 20}, 1, now32), 177 }, 178 {Metric: "Server{1,2}.memory.total", From: 0, Until: 1}: { 179 types.MakeMetricData("Server1.memory.total", []float64{4, 40, 25}, 1, now32), 180 types.MakeMetricData("Server2.memory.total", []float64{4, 20, 40}, 1, now32), 181 }, 182 }, 183 []*types.MetricData{ 184 types.MakeMetricData("asPercent(Server1.memory.used,Server1.memory.total)", []float64{25, 50, 60}, 1, now32), 185 types.MakeMetricData("asPercent(Server2.memory.used,Server2.memory.total)", []float64{25, 55, 50}, 1, now32), 186 types.MakeMetricData("asPercent(Server3.memory.used,MISSING)", []float64{NaN, NaN, NaN}, 1, now32), 187 }, 188 }, 189 // tagged series 190 { 191 "asPercent(seriesByTag('name=metric', 'tag=A*'),metricB*)", 192 map[parser.MetricRequest][]*types.MetricData{ 193 {Metric: "seriesByTag('name=metric', 'tag=A*')", From: 0, Until: 1}: { 194 types.MakeMetricData("metric;tag=A1", []float64{1, 20, 10}, 1, now32), 195 types.MakeMetricData("metric;tag=A2", []float64{1, 10, 20}, 1, now32), 196 }, 197 {Metric: "metricB*", From: 0, Until: 1}: { 198 types.MakeMetricData("metricB1", []float64{4, 4, 8}, 1, now32), 199 types.MakeMetricData("metricB2", []float64{4, 16, 2}, 1, now32), 200 }, 201 }, 202 []*types.MetricData{ 203 types.MakeMetricData("asPercent(metric;tag=A1,metricB1)", []float64{25, 500, 125}, 1, now32).SetTag("tag", "A1"), 204 types.MakeMetricData("asPercent(metric;tag=A2,metricB2)", []float64{25, 62.5, 1000}, 1, now32).SetTag("tag", "A2"), 205 }, 206 }, 207 } 208 209 for _, tt := range tests { 210 testName := tt.Target 211 t.Run(testName, func(t *testing.T) { 212 eval := th.EvaluatorFromFunc(md[0].F) 213 th.TestEvalExpr(t, eval, &tt) 214 }) 215 } 216 } 217 218 func TestAsPercentAlignment(t *testing.T) { 219 now32 := int64(time.Now().Unix()) 220 NaN := math.NaN() 221 testAlignments := []th.EvalTestItem{ 222 { 223 "asPercent(Server{1,2}.aligned.memory.used,Server{1,3}.aligned.memory.total)", 224 map[parser.MetricRequest][]*types.MetricData{ 225 {Metric: "Server{1,2}.aligned.memory.used", From: 0, Until: 1}: { 226 types.MakeMetricData("Server1.aligned.memory.used", []float64{1, 20, 10, 20}, 1, now32), 227 types.MakeMetricData("Server2.aligned.memory.used", []float64{0, 1, 10, 20}, 1, now32-1), 228 }, 229 {Metric: "Server{1,3}.aligned.memory.total", From: 0, Until: 1}: { 230 types.MakeMetricData("Server1.aligned.memory.total", []float64{1, 4, 4, 8}, 1, now32-1), 231 types.MakeMetricData("Server3.aligned.memory.total", []float64{4, 16, 2, 10}, 1, now32), 232 }, 233 }, 234 []*types.MetricData{ 235 types.MakeMetricData("asPercent(Server1.aligned.memory.used,Server1.aligned.memory.total)", []float64{NaN, 25, 500, 125, NaN}, 1, now32-1), 236 types.MakeMetricData("asPercent(Server2.aligned.memory.used,Server3.aligned.memory.total)", []float64{NaN, 25, 62.5, 1000, NaN}, 1, now32-1), 237 }, 238 }, 239 { 240 "asPercent(Server{1,2}.aligned.memory.used,Server3.aligned.memory.total)", 241 map[parser.MetricRequest][]*types.MetricData{ 242 {Metric: "Server{1,2}.aligned.memory.used", From: 0, Until: 1}: { 243 types.MakeMetricData("Server1.aligned.memory.used", []float64{1, 20, 10, 20}, 1, now32), 244 types.MakeMetricData("Server2.aligned.memory.used", []float64{0, 2, 10, 20}, 1, now32-1), 245 }, 246 {Metric: "Server3.aligned.memory.total", From: 0, Until: 1}: { 247 types.MakeMetricData("Server3.aligned.memory.total", []float64{4, 16, 2, 10, 40}, 1, now32-1), 248 }, 249 }, 250 []*types.MetricData{ 251 types.MakeMetricData("asPercent(Server1.aligned.memory.used,Server3.aligned.memory.total)", []float64{NaN, 6.25, 1000, 100, 50}, 1, now32-1), 252 types.MakeMetricData("asPercent(Server2.aligned.memory.used,Server3.aligned.memory.total)", []float64{0, 12.5, 500, 200, NaN}, 1, now32-1), 253 }, 254 }, 255 { 256 "asPercent(Server{1,2}.aligned.memory.used,100)", 257 map[parser.MetricRequest][]*types.MetricData{ 258 {Metric: "Server{1,2}.aligned.memory.used", From: 0, Until: 1}: { 259 types.MakeMetricData("Server1.aligned.memory.used", []float64{1, 20, 10, 20}, 1, now32), 260 types.MakeMetricData("Server2.aligned.memory.used", []float64{0, 1, 10, 20}, 1, now32-1), 261 }, 262 }, 263 []*types.MetricData{ 264 types.MakeMetricData("asPercent(Server1.aligned.memory.used,100)", []float64{NaN, 1, 20, 10, 20}, 1, now32-1), 265 types.MakeMetricData("asPercent(Server2.aligned.memory.used,100)", []float64{0, 1, 10, 20, NaN}, 1, now32-1), 266 }, 267 }, 268 } 269 270 for _, tt := range testAlignments { 271 testName := tt.Target 272 t.Run(testName, func(t *testing.T) { 273 eval := th.EvaluatorFromFunc(md[0].F) 274 th.TestEvalExpr(t, eval, &tt) 275 }) 276 } 277 } 278 279 func TestAsPercentGroup(t *testing.T) { 280 now32 := int64(time.Now().Unix()) 281 NaN := math.NaN() 282 283 tests := []th.EvalTestItem{ 284 { 285 "asPercent(Server{1,2}.memory.used,Server{1,3}.memory.total,0)", 286 map[parser.MetricRequest][]*types.MetricData{ 287 {Metric: "Server{1,2}.memory.used", From: 0, Until: 1}: { 288 types.MakeMetricData("Server1.memory.used", []float64{1, 20, 10}, 1, now32), 289 types.MakeMetricData("Server2.memory.used", []float64{1, 10, 20}, 1, now32), 290 }, 291 {Metric: "Server{1,3}.memory.total", From: 0, Until: 1}: { 292 types.MakeMetricData("Server1.memory.total", []float64{4, 4, 8}, 1, now32), 293 types.MakeMetricData("Server3.memory.total", []float64{4, 16, 2}, 1, now32), 294 }, 295 }, 296 []*types.MetricData{ 297 types.MakeMetricData("asPercent(Server1.memory.used,Server1.memory.total)", []float64{25, 500, 125}, 1, now32), 298 types.MakeMetricData("asPercent(Server2.memory.used,MISSING)", []float64{NaN, NaN, NaN}, 1, now32), 299 types.MakeMetricData("asPercent(MISSING,Server3.memory.total)", []float64{NaN, NaN, NaN}, 1, now32), 300 }, 301 }, 302 303 // Broken tests with current 304 { 305 "asPercent(Server{1,2}.memory.{used,free},Server{1,3}.memory.total,0)", 306 map[parser.MetricRequest][]*types.MetricData{ 307 {Metric: "Server{1,2}.memory.{used,free}", From: 0, Until: 1}: { 308 types.MakeMetricData("Server1.memory.used", []float64{1, 20, 10}, 1, now32), 309 types.MakeMetricData("Server1.memory.free", []float64{1, 20, 10}, 1, now32), 310 types.MakeMetricData("Server2.memory.used", []float64{1, 10, 20}, 1, now32), 311 types.MakeMetricData("Server2.memory.free", []float64{1, 20, 10}, 1, now32), 312 }, 313 {Metric: "Server{1,3}.memory.total", From: 0, Until: 1}: { 314 types.MakeMetricData("Server1.memory.total", []float64{4, 4, 8}, 1, now32), 315 types.MakeMetricData("Server3.memory.total", []float64{4, 16, 2}, 1, now32), 316 }, 317 }, 318 []*types.MetricData{ 319 types.MakeMetricData("asPercent(Server1.memory.free,Server1.memory.total)", []float64{25, 500, 125}, 1, now32), 320 types.MakeMetricData("asPercent(Server1.memory.used,Server1.memory.total)", []float64{25, 500, 125}, 1, now32), 321 types.MakeMetricData("asPercent(Server2.memory.free,MISSING)", []float64{NaN, NaN, NaN}, 1, now32), 322 types.MakeMetricData("asPercent(Server2.memory.used,MISSING)", []float64{NaN, NaN, NaN}, 1, now32), 323 types.MakeMetricData("asPercent(MISSING,Server3.memory.total)", []float64{NaN, NaN, NaN}, 1, now32), 324 }, 325 }, 326 { 327 "asPercent(Server{1,2}.memory.{used,free},None,0)", 328 map[parser.MetricRequest][]*types.MetricData{ 329 {Metric: "Server{1,2}.memory.{used,free}", From: 0, Until: 1}: { 330 types.MakeMetricData("Server1.memory.used", []float64{2, 1, NaN}, 1, now32), 331 types.MakeMetricData("Server1.memory.free", []float64{3, NaN, 8}, 1, now32), 332 types.MakeMetricData("Server2.memory.used", []float64{4, NaN, 2}, 1, now32), 333 }, 334 }, 335 []*types.MetricData{ 336 types.MakeMetricData("asPercent(Server1.memory.free,None)", []float64{60, NaN, 100}, 1, now32), 337 types.MakeMetricData("asPercent(Server1.memory.used,None)", []float64{40, 100, NaN}, 1, now32), 338 types.MakeMetricData("asPercent(Server2.memory.used,None)", []float64{100, NaN, 100}, 1, now32), 339 }, 340 }, 341 } 342 343 for _, tt := range tests { 344 testName := tt.Target 345 t.Run(testName, func(t *testing.T) { 346 eval := th.EvaluatorFromFunc(md[0].F) 347 th.TestEvalExpr(t, eval, &tt) 348 }) 349 } 350 } 351 352 func BenchmarkAsPercent(b *testing.B) { 353 NaN := math.NaN() 354 benchmarks := []struct { 355 target string 356 M map[parser.MetricRequest][]*types.MetricData 357 }{ 358 { 359 target: "asPercent(metric*)", 360 M: map[parser.MetricRequest][]*types.MetricData{ 361 {Metric: "metric*", From: 0, Until: 1}: { 362 types.MakeMetricData("metric1", []float64{1, NaN, NaN, 3, 4, 14}, 1, 1), 363 types.MakeMetricData("metric2", []float64{4, NaN, 3, NaN, 0, 6}, 1, 1), 364 }, 365 }, 366 }, 367 { 368 target: "asPercent(metric1,metric2)", 369 M: map[parser.MetricRequest][]*types.MetricData{ 370 {Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, NaN, NaN, 3, 4, 12}, 1, 1)}, 371 {Metric: "metric2", From: 0, Until: 1}: {types.MakeMetricData("metric2", []float64{2, NaN, 3, NaN, 0, 6}, 1, 1)}, 372 }, 373 }, 374 { 375 target: "asPercent(Server{1,2}.memory.used,Server{1,2,3}.memory.total)", 376 M: map[parser.MetricRequest][]*types.MetricData{ 377 {Metric: "Server{1,2}.memory.used", From: 0, Until: 1}: { 378 types.MakeMetricData("Server1.aligned.memory.used", []float64{1, 20, 10, 20}, 1, 2), 379 types.MakeMetricData("Server2.aligned.memory.used", []float64{0, 1, 10, 20}, 1, 1), 380 }, 381 {Metric: "Server{1,2,3}.memory.total", From: 0, Until: 1}: { 382 types.MakeMetricData("Server1.aligned.memory.total", []float64{1, 4, 4, 8}, 1, 1), 383 types.MakeMetricData("Server2.aligned.memory.total", []float64{4, 16, 2, 10}, 1, 2), 384 types.MakeMetricData("Server3.aligned.memory.total", []float64{4, 16, 2, 10}, 1, 2), 385 }, 386 }, 387 }, 388 { 389 target: "asPercent(Server{1,2,3}.memory.used,Server{1,2}.memory.total)", 390 M: map[parser.MetricRequest][]*types.MetricData{ 391 {Metric: "Server{1,2,3}.memory.used", From: 0, Until: 1}: { 392 types.MakeMetricData("Server1.aligned.memory.used", []float64{1, 20, 10, 20}, 1, 2), 393 types.MakeMetricData("Server2.aligned.memory.used", []float64{0, 1, 10, 20}, 1, 1), 394 types.MakeMetricData("Server3.aligned.memory.used", []float64{0, 1, 10, 20}, 1, 1), 395 }, 396 {Metric: "Server{1,2}.memory.total", From: 0, Until: 1}: { 397 types.MakeMetricData("Server1.aligned.memory.total", []float64{1, 4, 4, 8}, 1, 1), 398 types.MakeMetricData("Server2.aligned.memory.total", []float64{4, 16, 2, 10}, 1, 2), 399 }, 400 }, 401 }, 402 { 403 target: "asPercent(Server{1,2}.memory.{used,free},Server{1,2}.memory.total)", 404 M: map[parser.MetricRequest][]*types.MetricData{ 405 {Metric: "Server{1,2}.memory.{used,free}", From: 0, Until: 1}: { 406 types.MakeMetricData("Server1.aligned.memory.used", []float64{1, 20, 10, 20}, 1, 2), 407 types.MakeMetricData("Server2.aligned.memory.used", []float64{0, 1, 10, 20}, 1, 1), 408 types.MakeMetricData("Server1.aligned.memory.free", []float64{1, 20, 10, 20}, 1, 2), 409 types.MakeMetricData("Server2.aligned.memory.free", []float64{0, 1, 10, 20}, 1, 1), 410 }, 411 {Metric: "Server{1,2}.memory.total", From: 0, Until: 1}: { 412 types.MakeMetricData("Server1.aligned.memory.total", []float64{1, 4, 4, 8}, 1, 1), 413 types.MakeMetricData("Server2.aligned.memory.total", []float64{4, 16, 2, 10}, 1, 2), 414 }, 415 }, 416 }, 417 { 418 target: "asPercent(Server{1,2}.aligned.memory.used,Server{1,3}.aligned.memory.total)", 419 M: map[parser.MetricRequest][]*types.MetricData{ 420 {Metric: "Server{1,2}.aligned.memory.used", From: 0, Until: 1}: { 421 types.MakeMetricData("Server1.aligned.memory.used", []float64{1, 20, 10, 20}, 1, 2), 422 types.MakeMetricData("Server2.aligned.memory.used", []float64{0, 1, 10, 20}, 1, 1), 423 }, 424 {Metric: "Server{1,3}.aligned.memory.total", From: 0, Until: 1}: { 425 types.MakeMetricData("Server1.aligned.memory.total", []float64{1, 4, 4, 8}, 1, 1), 426 types.MakeMetricData("Server3.aligned.memory.total", []float64{4, 16, 2, 10}, 1, 2), 427 }, 428 }, 429 }, 430 { 431 target: "asPercent(Server{1,2}.aligned.memory.used,Server3.aligned.memory.total)", 432 M: map[parser.MetricRequest][]*types.MetricData{ 433 {Metric: "Server{1,2}.aligned.memory.used", From: 0, Until: 1}: { 434 types.MakeMetricData("Server1.aligned.memory.used", []float64{1, 20, 10, 20}, 1, 2), 435 types.MakeMetricData("Server2.aligned.memory.used", []float64{0, 2, 10, 20}, 1, 1), 436 }, 437 {Metric: "Server3.aligned.memory.total", From: 0, Until: 1}: { 438 types.MakeMetricData("Server3.aligned.memory.total", []float64{4, 16, 2, 10, 40}, 1, 1), 439 }, 440 }, 441 }, 442 { 443 target: `asPercent(Server{1,2}.aligned.memory.used,100)`, 444 M: map[parser.MetricRequest][]*types.MetricData{ 445 {Metric: "Server{1,2}.aligned.memory.used", From: 0, Until: 1}: { 446 types.MakeMetricData("Server1.aligned.memory.used", []float64{1, 20, 10, 20, 1, 20, 10, 20, 1, 20, 10, 20}, 1, 2), 447 types.MakeMetricData("Server2.aligned.memory.used", []float64{0, 1, 10, 20, 0, 1, 10, 20, 0, 1, 10, 20}, 1, 1), 448 }, 449 }, 450 }, 451 } 452 453 eval := th.EvaluatorFromFunc(md[0].F) 454 455 for _, bm := range benchmarks { 456 b.Run(bm.target, func(b *testing.B) { 457 exp, _, err := parser.ParseExpr(bm.target) 458 if err != nil { 459 b.Fatalf("failed to parse %s: %+v", bm.target, err) 460 } 461 462 b.ResetTimer() 463 464 for i := 0; i < b.N; i++ { 465 g, err := eval.Eval(context.Background(), exp, 0, 1, bm.M) 466 if err != nil { 467 b.Fatalf("failed to eval %s: %+v", bm.target, err) 468 } 469 _ = g 470 } 471 }) 472 } 473 } 474 475 func BenchmarkAsPercentGroup(b *testing.B) { 476 NaN := math.NaN() 477 benchmarks := []struct { 478 target string 479 M map[parser.MetricRequest][]*types.MetricData 480 }{ 481 { 482 target: "asPercent(Server{1,2}.memory.used,Server{1,3}.memory.total,0)", 483 M: map[parser.MetricRequest][]*types.MetricData{ 484 {Metric: "Server{1,2}.memory.used", From: 0, Until: 1}: { 485 types.MakeMetricData("Server1.memory.used", []float64{1, 20, 10}, 1, 1), 486 types.MakeMetricData("Server2.memory.used", []float64{1, 10, 20}, 1, 1), 487 }, 488 {Metric: "Server{1,3}.memory.total", From: 0, Until: 1}: { 489 types.MakeMetricData("Server1.memory.total", []float64{4, 4, 8}, 1, 1), 490 types.MakeMetricData("Server3.memory.total", []float64{4, 16, 2}, 1, 1), 491 }, 492 }, 493 }, 494 { 495 target: "asPercent(Server{1,2}.memory.{used,free},None,0)", 496 M: map[parser.MetricRequest][]*types.MetricData{ 497 {Metric: "Server{1,2}.memory.{used,free}", From: 0, Until: 1}: { 498 types.MakeMetricData("Server1.memory.used", []float64{2, 1, NaN}, 1, 1), 499 types.MakeMetricData("Server1.memory.free", []float64{3, NaN, 8}, 1, 1), 500 types.MakeMetricData("Server2.memory.used", []float64{4, NaN, 2}, 1, 1), 501 }, 502 }, 503 }, 504 } 505 506 eval := th.EvaluatorFromFunc(md[0].F) 507 508 for _, bm := range benchmarks { 509 b.Run(bm.target, func(b *testing.B) { 510 exp, _, err := parser.ParseExpr(bm.target) 511 if err != nil { 512 b.Fatalf("failed to parse %s: %+v", bm.target, err) 513 } 514 515 b.ResetTimer() 516 517 for i := 0; i < b.N; i++ { 518 g, err := eval.Eval(context.Background(), exp, 0, 1, bm.M) 519 if err != nil { 520 b.Fatalf("failed to eval %s: %+v", bm.target, err) 521 } 522 _ = g 523 } 524 }) 525 } 526 }