github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/querier/queryrange/queryrangebase/promql_test.go (about) 1 package queryrangebase 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 "sort" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/go-kit/log" 13 "github.com/prometheus/client_golang/prometheus" 14 "github.com/prometheus/prometheus/model/labels" 15 "github.com/prometheus/prometheus/promql" 16 "github.com/prometheus/prometheus/storage" 17 "github.com/stretchr/testify/require" 18 19 "github.com/grafana/loki/pkg/querier/astmapper" 20 ) 21 22 var ( 23 start = time.Unix(1000, 0) 24 end = start.Add(3 * time.Minute) 25 step = 30 * time.Second 26 ctx = context.Background() 27 engine = promql.NewEngine(promql.EngineOpts{ 28 Reg: prometheus.DefaultRegisterer, 29 Logger: log.NewNopLogger(), 30 Timeout: 1 * time.Hour, 31 MaxSamples: 10e6, 32 ActiveQueryTracker: nil, 33 }) 34 ) 35 36 // This test allows to verify which PromQL expressions can be parallelized. 37 func Test_PromQL(t *testing.T) { 38 t.Parallel() 39 40 var tests = []struct { 41 normalQuery string 42 shardQuery string 43 shouldEqual bool 44 }{ 45 // Vector can be parallelized but we need to remove the cortex shard label. 46 // It should be noted that the __cortex_shard__ label is required by the engine 47 // and therefore should be returned by the storage. 48 // Range vectors `bar1{baz="blip"}[1m]` are not tested here because it is not supported 49 // by range queries. 50 { 51 `bar1{baz="blip"}`, 52 `label_replace( 53 bar1{__cortex_shard__="0_of_3",baz="blip"} or 54 bar1{__cortex_shard__="1_of_3",baz="blip"} or 55 bar1{__cortex_shard__="2_of_3",baz="blip"}, 56 "__cortex_shard__","","","" 57 )`, 58 true, 59 }, 60 // __cortex_shard__ label is required otherwise the or will keep only the first series. 61 { 62 `sum(bar1{baz="blip"})`, 63 `sum( 64 sum (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 65 sum (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 66 sum (bar1{__cortex_shard__="2_of_3",baz="blip"}) 67 )`, 68 false, 69 }, 70 { 71 `sum(bar1{baz="blip"})`, 72 `sum( 73 sum without(__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 74 sum without(__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 75 sum without(__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 76 )`, 77 true, 78 }, 79 { 80 `sum by (foo) (bar1{baz="blip"})`, 81 `sum by (foo) ( 82 sum by(foo,__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 83 sum by(foo,__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 84 sum by(foo,__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 85 )`, 86 true, 87 }, 88 { 89 `sum by (foo,bar) (bar1{baz="blip"})`, 90 `sum by (foo,bar)( 91 sum by(foo,bar,__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 92 sum by(foo,bar,__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 93 sum by(foo,bar,__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 94 )`, 95 true, 96 }, 97 // since series are unique to a shard, it's safe to sum without shard first, then reaggregate 98 { 99 `sum without (foo,bar) (bar1{baz="blip"})`, 100 `sum without (foo,bar)( 101 sum without(__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 102 sum without(__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 103 sum without(__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 104 )`, 105 true, 106 }, 107 { 108 `min by (foo,bar) (bar1{baz="blip"})`, 109 `min by (foo,bar)( 110 min by(foo,bar,__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 111 min by(foo,bar,__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 112 min by(foo,bar,__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 113 )`, 114 true, 115 }, 116 { 117 `max by (foo,bar) (bar1{baz="blip"})`, 118 ` max by (foo,bar)( 119 max by(foo,bar,__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 120 max by(foo,bar,__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 121 max by(foo,bar,__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 122 )`, 123 true, 124 }, 125 // avg generally cant be parallelized 126 { 127 `avg(bar1{baz="blip"})`, 128 `avg( 129 avg by(__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 130 avg by(__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 131 avg by(__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 132 )`, 133 false, 134 }, 135 // stddev can't be parallelized. 136 { 137 `stddev(bar1{baz="blip"})`, 138 ` stddev( 139 stddev by(__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 140 stddev by(__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 141 stddev by(__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 142 )`, 143 false, 144 }, 145 // stdvar can't be parallelized. 146 { 147 `stdvar(bar1{baz="blip"})`, 148 `stdvar( 149 stdvar by(__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 150 stdvar by(__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 151 stdvar by(__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 152 )`, 153 false, 154 }, 155 { 156 `count(bar1{baz="blip"})`, 157 `count( 158 count without (__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 159 count without (__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 160 count without (__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 161 )`, 162 true, 163 }, 164 { 165 `count by (foo,bar) (bar1{baz="blip"})`, 166 `count by (foo,bar) ( 167 count by (foo,bar,__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 168 count by (foo,bar,__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 169 count by (foo,bar,__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 170 )`, 171 true, 172 }, 173 // different ways to represent count without. 174 { 175 `count without (foo) (bar1{baz="blip"})`, 176 `count without (foo) ( 177 count without (__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 178 count without (__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 179 count without (__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 180 )`, 181 true, 182 }, 183 { 184 `count without (foo) (bar1{baz="blip"})`, 185 `sum without (__cortex_shard__) ( 186 count without (foo) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 187 count without (foo) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 188 count without (foo) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 189 )`, 190 true, 191 }, 192 { 193 `count without (foo, bar) (bar1{baz="blip"})`, 194 `count without (foo, bar) ( 195 count without (__cortex_shard__) (bar1{__cortex_shard__="0_of_3",baz="blip"}) or 196 count without (__cortex_shard__) (bar1{__cortex_shard__="1_of_3",baz="blip"}) or 197 count without (__cortex_shard__) (bar1{__cortex_shard__="2_of_3",baz="blip"}) 198 )`, 199 true, 200 }, 201 { 202 `topk(2,bar1{baz="blip"})`, 203 `label_replace( 204 topk(2, 205 topk(2,(bar1{__cortex_shard__="0_of_3",baz="blip"})) without(__cortex_shard__) or 206 topk(2,(bar1{__cortex_shard__="1_of_3",baz="blip"})) without(__cortex_shard__) or 207 topk(2,(bar1{__cortex_shard__="2_of_3",baz="blip"})) without(__cortex_shard__) 208 ), 209 "__cortex_shard__","","","")`, 210 true, 211 }, 212 { 213 `bottomk(2,bar1{baz="blip"})`, 214 `label_replace( 215 bottomk(2, 216 bottomk(2,(bar1{__cortex_shard__="0_of_3",baz="blip"})) without(__cortex_shard__) or 217 bottomk(2,(bar1{__cortex_shard__="1_of_3",baz="blip"})) without(__cortex_shard__) or 218 bottomk(2,(bar1{__cortex_shard__="2_of_3",baz="blip"})) without(__cortex_shard__) 219 ), 220 "__cortex_shard__","","","")`, 221 true, 222 }, 223 { 224 `sum by (foo,bar) (avg_over_time(bar1{baz="blip"}[1m]))`, 225 `sum by (foo,bar)( 226 sum by(foo,bar,__cortex_shard__) (avg_over_time(bar1{__cortex_shard__="0_of_3",baz="blip"}[1m])) or 227 sum by(foo,bar,__cortex_shard__) (avg_over_time(bar1{__cortex_shard__="1_of_3",baz="blip"}[1m])) or 228 sum by(foo,bar,__cortex_shard__) (avg_over_time(bar1{__cortex_shard__="2_of_3",baz="blip"}[1m])) 229 )`, 230 true, 231 }, 232 { 233 `sum by (foo,bar) (min_over_time(bar1{baz="blip"}[1m]))`, 234 `sum by (foo,bar)( 235 sum by(foo,bar,__cortex_shard__) (min_over_time(bar1{__cortex_shard__="0_of_3",baz="blip"}[1m])) or 236 sum by(foo,bar,__cortex_shard__) (min_over_time(bar1{__cortex_shard__="1_of_3",baz="blip"}[1m])) or 237 sum by(foo,bar,__cortex_shard__) (min_over_time(bar1{__cortex_shard__="2_of_3",baz="blip"}[1m])) 238 )`, 239 true, 240 }, 241 { 242 // Sub aggregations must avoid non-associative series merging across shards 243 `sum( 244 count( 245 bar1 246 ) by (foo,bazz) 247 )`, 248 ` 249 sum without(__cortex_shard__) ( 250 sum by(__cortex_shard__) ( 251 count by(foo, bazz) (foo{__cortex_shard__="0_of_2",bar="baz"}) 252 ) or 253 sum by(__cortex_shard__) ( 254 count by(foo, bazz) (foo{__cortex_shard__="1_of_2",bar="baz"}) 255 ) 256 ) 257 `, 258 false, 259 }, 260 { 261 // Note: this is a speculative optimization that we don't currently include due to mapping complexity. 262 // Certain sub aggregations may inject __cortex_shard__ for all (by) subgroupings. 263 // This is the same as the previous test with the exception that the shard label is injected to the count grouping 264 `sum( 265 count( 266 bar1 267 ) by (foo,bazz) 268 )`, 269 ` 270 sum without(__cortex_shard__) ( 271 sum by(__cortex_shard__) ( 272 count by(foo, bazz, __cortex_shard__) (foo{__cortex_shard__="0_of_2",bar="baz"}) 273 ) or 274 sum by(__cortex_shard__) ( 275 count by(foo, bazz, __cortex_shard__) (foo{__cortex_shard__="1_of_2",bar="baz"}) 276 ) 277 ) 278 `, 279 true, 280 }, 281 { 282 // Note: this is a speculative optimization that we don't currently include due to mapping complexity 283 // This example details multiple layers of aggregations. 284 // Sub aggregations must inject __cortex_shard__ for all (by) subgroupings. 285 `sum( 286 count( 287 count( 288 bar1 289 ) by (foo,bazz) 290 ) by (bazz) 291 )`, 292 ` 293 sum without(__cortex_shard__) ( 294 sum by(__cortex_shard__) ( 295 count by(bazz, __cortex_shard__) ( 296 count by(foo, bazz, __cortex_shard__) ( 297 foo{__cortex_shard__="0_of_2", bar="baz"} 298 ) 299 ) 300 ) or 301 sum by(__cortex_shard__) ( 302 count by(bazz, __cortex_shard__) ( 303 count by(foo, bazz, __cortex_shard__) ( 304 foo{__cortex_shard__="1_of_2", bar="baz"} 305 ) 306 ) 307 ) 308 ) 309 `, 310 true, 311 }, 312 } 313 314 for _, tt := range tests { 315 tt := tt 316 t.Run(tt.normalQuery, func(t *testing.T) { 317 318 baseQuery, err := engine.NewRangeQuery(shardAwareQueryable, nil, tt.normalQuery, start, end, step) 319 require.Nil(t, err) 320 shardQuery, err := engine.NewRangeQuery(shardAwareQueryable, nil, tt.shardQuery, start, end, step) 321 require.Nil(t, err) 322 baseResult := baseQuery.Exec(ctx) 323 shardResult := shardQuery.Exec(ctx) 324 t.Logf("base: %v\n", baseResult) 325 t.Logf("shard: %v\n", shardResult) 326 if tt.shouldEqual { 327 require.Equal(t, baseResult, shardResult) 328 return 329 } 330 require.NotEqual(t, baseResult, shardResult) 331 }) 332 } 333 334 } 335 336 func Test_FunctionParallelism(t *testing.T) { 337 tpl := `sum(<fn>(bar1{}<fArgs>))` 338 shardTpl := `sum( 339 sum without(__cortex_shard__) (<fn>(bar1{__cortex_shard__="0_of_3"}<fArgs>)) or 340 sum without(__cortex_shard__) (<fn>(bar1{__cortex_shard__="1_of_3"}<fArgs>)) or 341 sum without(__cortex_shard__) (<fn>(bar1{__cortex_shard__="2_of_3"}<fArgs>)) 342 )` 343 344 mkQuery := func(tpl, fn string, testMatrix bool, fArgs []string) (result string) { 345 result = strings.Replace(tpl, "<fn>", fn, -1) 346 347 if testMatrix { 348 // turn selectors into ranges 349 result = strings.Replace(result, "}<fArgs>", "}[1m]<fArgs>", -1) 350 } 351 352 if len(fArgs) > 0 { 353 args := "," + strings.Join(fArgs, ",") 354 result = strings.Replace(result, "<fArgs>", args, -1) 355 } else { 356 result = strings.Replace(result, "<fArgs>", "", -1) 357 } 358 359 return result 360 } 361 362 for _, tc := range []struct { 363 fn string 364 fArgs []string 365 isTestMatrix bool 366 approximate bool 367 }{ 368 { 369 fn: "abs", 370 }, 371 { 372 fn: "avg_over_time", 373 isTestMatrix: true, 374 approximate: true, 375 }, 376 { 377 fn: "ceil", 378 }, 379 { 380 fn: "changes", 381 isTestMatrix: true, 382 }, 383 { 384 fn: "count_over_time", 385 isTestMatrix: true, 386 }, 387 { 388 fn: "days_in_month", 389 }, 390 { 391 fn: "day_of_month", 392 }, 393 { 394 fn: "day_of_week", 395 }, 396 { 397 fn: "delta", 398 isTestMatrix: true, 399 approximate: true, 400 }, 401 { 402 fn: "deriv", 403 isTestMatrix: true, 404 approximate: true, 405 }, 406 { 407 fn: "exp", 408 approximate: true, 409 }, 410 { 411 fn: "floor", 412 }, 413 { 414 fn: "hour", 415 }, 416 { 417 fn: "idelta", 418 isTestMatrix: true, 419 approximate: true, 420 }, 421 { 422 fn: "increase", 423 isTestMatrix: true, 424 approximate: true, 425 }, 426 { 427 fn: "irate", 428 isTestMatrix: true, 429 approximate: true, 430 }, 431 { 432 fn: "ln", 433 approximate: true, 434 }, 435 { 436 fn: "log10", 437 approximate: true, 438 }, 439 { 440 fn: "log2", 441 approximate: true, 442 }, 443 { 444 fn: "max_over_time", 445 isTestMatrix: true, 446 }, 447 { 448 fn: "min_over_time", 449 isTestMatrix: true, 450 }, 451 { 452 fn: "minute", 453 }, 454 { 455 fn: "month", 456 }, 457 { 458 fn: "rate", 459 isTestMatrix: true, 460 approximate: true, 461 }, 462 { 463 fn: "resets", 464 isTestMatrix: true, 465 }, 466 { 467 fn: "sort", 468 }, 469 { 470 fn: "sort_desc", 471 }, 472 { 473 fn: "sqrt", 474 approximate: true, 475 }, 476 { 477 fn: "stddev_over_time", 478 isTestMatrix: true, 479 approximate: true, 480 }, 481 { 482 fn: "stdvar_over_time", 483 isTestMatrix: true, 484 approximate: true, 485 }, 486 { 487 fn: "sum_over_time", 488 isTestMatrix: true, 489 }, 490 { 491 fn: "timestamp", 492 }, 493 { 494 fn: "year", 495 }, 496 { 497 fn: "clamp_max", 498 fArgs: []string{"5"}, 499 }, 500 { 501 fn: "clamp_min", 502 fArgs: []string{"5"}, 503 }, 504 { 505 fn: "predict_linear", 506 isTestMatrix: true, 507 approximate: true, 508 fArgs: []string{"1"}, 509 }, 510 { 511 fn: "round", 512 fArgs: []string{"20"}, 513 }, 514 { 515 fn: "holt_winters", 516 isTestMatrix: true, 517 fArgs: []string{"0.5", "0.7"}, 518 approximate: true, 519 }, 520 } { 521 522 t.Run(tc.fn, func(t *testing.T) { 523 baseQuery, err := engine.NewRangeQuery( 524 shardAwareQueryable, 525 nil, 526 mkQuery(tpl, tc.fn, tc.isTestMatrix, tc.fArgs), 527 start, 528 end, 529 step, 530 ) 531 require.Nil(t, err) 532 shardQuery, err := engine.NewRangeQuery( 533 shardAwareQueryable, 534 nil, 535 mkQuery(shardTpl, tc.fn, tc.isTestMatrix, tc.fArgs), 536 start, 537 end, 538 step, 539 ) 540 require.Nil(t, err) 541 baseResult := baseQuery.Exec(ctx) 542 shardResult := shardQuery.Exec(ctx) 543 t.Logf("base: %+v\n", baseResult) 544 t.Logf("shard: %+v\n", shardResult) 545 if !tc.approximate { 546 require.Equal(t, baseResult, shardResult) 547 } else { 548 // Some functions yield tiny differences when sharded due to combining floating point calculations. 549 baseSeries := baseResult.Value.(promql.Matrix)[0] 550 shardSeries := shardResult.Value.(promql.Matrix)[0] 551 552 require.Equal(t, len(baseSeries.Points), len(shardSeries.Points)) 553 for i, basePt := range baseSeries.Points { 554 shardPt := shardSeries.Points[i] 555 require.Equal(t, basePt.T, shardPt.T) 556 require.Equal( 557 t, 558 math.Round(basePt.V*1e6)/1e6, 559 math.Round(shardPt.V*1e6)/1e6, 560 ) 561 } 562 563 } 564 }) 565 } 566 567 } 568 569 var shardAwareQueryable = storage.QueryableFunc(func(ctx context.Context, mint, maxt int64) (storage.Querier, error) { 570 return &testMatrix{ 571 series: []*promql.StorageSeries{ 572 newSeries(labels.Labels{{Name: "__name__", Value: "bar1"}, {Name: "baz", Value: "blip"}, {Name: "bar", Value: "blop"}, {Name: "foo", Value: "barr"}}, factor(5)), 573 newSeries(labels.Labels{{Name: "__name__", Value: "bar1"}, {Name: "baz", Value: "blip"}, {Name: "bar", Value: "blop"}, {Name: "foo", Value: "bazz"}}, factor(7)), 574 newSeries(labels.Labels{{Name: "__name__", Value: "bar1"}, {Name: "baz", Value: "blip"}, {Name: "bar", Value: "blap"}, {Name: "foo", Value: "buzz"}}, factor(12)), 575 newSeries(labels.Labels{{Name: "__name__", Value: "bar1"}, {Name: "baz", Value: "blip"}, {Name: "bar", Value: "blap"}, {Name: "foo", Value: "bozz"}}, factor(11)), 576 newSeries(labels.Labels{{Name: "__name__", Value: "bar1"}, {Name: "baz", Value: "blip"}, {Name: "bar", Value: "blop"}, {Name: "foo", Value: "buzz"}}, factor(8)), 577 newSeries(labels.Labels{{Name: "__name__", Value: "bar1"}, {Name: "baz", Value: "blip"}, {Name: "bar", Value: "blap"}, {Name: "foo", Value: "bazz"}}, identity), 578 }, 579 }, nil 580 }) 581 582 type testMatrix struct { 583 series []*promql.StorageSeries 584 } 585 586 func (m *testMatrix) Copy() *testMatrix { 587 cpy := *m 588 return &cpy 589 } 590 591 func (m testMatrix) Next() bool { return len(m.series) != 0 } 592 593 func (m *testMatrix) At() storage.Series { 594 res := m.series[0] 595 m.series = m.series[1:] 596 return res 597 } 598 599 func (m *testMatrix) Err() error { return nil } 600 601 func (m *testMatrix) Warnings() storage.Warnings { return nil } 602 603 func (m *testMatrix) Select(_ bool, selectParams *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet { 604 s, _, err := astmapper.ShardFromMatchers(matchers) 605 if err != nil { 606 return storage.ErrSeriesSet(err) 607 } 608 609 if s != nil { 610 return splitByShard(s.Shard, s.Of, m) 611 } 612 613 return m.Copy() 614 } 615 616 func (m *testMatrix) LabelValues(name string, matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { 617 return nil, nil, nil 618 } 619 func (m *testMatrix) LabelNames(matchers ...*labels.Matcher) ([]string, storage.Warnings, error) { 620 return nil, nil, nil 621 } 622 func (m *testMatrix) Close() error { return nil } 623 624 func newSeries(metric labels.Labels, generator func(float64) float64) *promql.StorageSeries { 625 sort.Sort(metric) 626 var points []promql.Point 627 628 for ts := start.Add(-step); ts.Unix() <= end.Unix(); ts = ts.Add(step) { 629 t := ts.Unix() * 1e3 630 points = append(points, promql.Point{ 631 T: t, 632 V: generator(float64(t)), 633 }) 634 } 635 636 return promql.NewStorageSeries(promql.Series{ 637 Metric: metric, 638 Points: points, 639 }) 640 } 641 642 func identity(t float64) float64 { 643 return t 644 } 645 646 func factor(f float64) func(float64) float64 { 647 i := 0. 648 return func(float64) float64 { 649 i++ 650 res := i * f 651 return res 652 } 653 } 654 655 // var identity(t int64) float64 { 656 // return float64(t) 657 // } 658 659 // splitByShard returns the shard subset of a testMatrix. 660 // e.g if a testMatrix has 6 series, and we want 3 shard, then each shard will contain 661 // 2 series. 662 func splitByShard(shardIndex, shardTotal int, testMatrices *testMatrix) *testMatrix { 663 res := &testMatrix{} 664 for i, s := range testMatrices.series { 665 if i%shardTotal != shardIndex { 666 continue 667 } 668 var points []promql.Point 669 it := s.Iterator() 670 for it.Next() { 671 t, v := it.At() 672 points = append(points, promql.Point{ 673 T: t, 674 V: v, 675 }) 676 677 } 678 lbs := s.Labels().Copy() 679 lbs = append(lbs, labels.Label{Name: "__cortex_shard__", Value: fmt.Sprintf("%d_of_%d", shardIndex, shardTotal)}) 680 sort.Sort(lbs) 681 res.series = append(res.series, promql.NewStorageSeries(promql.Series{ 682 Metric: lbs, 683 Points: points, 684 })) 685 } 686 return res 687 }