github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ts/query_test.go (about) 1 // Copyright 2015 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package ts 12 13 import ( 14 "context" 15 "math" 16 "runtime" 17 "testing" 18 "time" 19 20 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 21 "github.com/cockroachdb/cockroach/pkg/ts/tspb" 22 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 23 "github.com/cockroachdb/cockroach/pkg/util/mon" 24 ) 25 26 // runTestCaseMultipleFormats runs the provided test case body against a 27 // testModelRunner configured to run in row format, and then again against a 28 // cluster configured to run in column format. 29 func runTestCaseMultipleFormats(t *testing.T, testCase func(*testing.T, testModelRunner)) { 30 t.Run("Row Format", func(t *testing.T) { 31 tm := newTestModelRunner(t) 32 tm.Start() 33 tm.DB.forceRowFormat = true 34 defer tm.Stop() 35 testCase(t, tm) 36 }) 37 38 t.Run("Column Format", func(t *testing.T) { 39 tm := newTestModelRunner(t) 40 tm.Start() 41 defer tm.Stop() 42 testCase(t, tm) 43 }) 44 } 45 46 // TestQueryBasic validates that query results match the expectation of the test 47 // model in basic situations. 48 func TestQueryBasic(t *testing.T) { 49 defer leaktest.AfterTest(t)() 50 runTestCaseMultipleFormats(t, func(t *testing.T, tm testModelRunner) { 51 tm.storeTimeSeriesData(resolution1ns, []tspb.TimeSeriesData{ 52 tsd("test.metric", "", 53 tsdp(1, 100), 54 tsdp(5, 200), 55 tsdp(15, 300), 56 tsdp(16, 400), 57 tsdp(17, 500), 58 tsdp(22, 600), 59 tsdp(52, 900), 60 ), 61 }) 62 tm.assertKeyCount(4) 63 tm.assertModelCorrect() 64 65 query := tm.makeQuery("test.metric", resolution1ns, 0, 60) 66 query.assertSuccess(7, 1) 67 68 // Verify across multiple sources 69 tm.storeTimeSeriesData(resolution1ns, []tspb.TimeSeriesData{ 70 tsd("test.multimetric", "source1", 71 tsdp(1, 100), 72 tsdp(15, 300), 73 tsdp(17, 500), 74 tsdp(52, 900), 75 ), 76 tsd("test.multimetric", "source2", 77 tsdp(5, 100), 78 tsdp(16, 300), 79 tsdp(22, 500), 80 tsdp(82, 900), 81 ), 82 }) 83 84 tm.assertKeyCount(11) 85 tm.assertModelCorrect() 86 87 // Test default query: avg downsampler, sum aggregator, no derivative. 88 query = tm.makeQuery("test.multimetric", resolution1ns, 0, 90) 89 query.assertSuccess(8, 2) 90 // Test with aggregator specified. 91 query.setSourceAggregator(tspb.TimeSeriesQueryAggregator_MAX) 92 query.assertSuccess(8, 2) 93 // Test with aggregator and downsampler. 94 query.setDownsampler(tspb.TimeSeriesQueryAggregator_AVG) 95 query.assertSuccess(8, 2) 96 // Test with derivative specified. 97 query.Downsampler = nil 98 query.setDerivative(tspb.TimeSeriesQueryDerivative_DERIVATIVE) 99 query.assertSuccess(7, 2) 100 // Test with everything specified. 101 query = tm.makeQuery("test.multimetric", resolution1ns, 0, 90) 102 query.setSourceAggregator(tspb.TimeSeriesQueryAggregator_MIN) 103 query.setDownsampler(tspb.TimeSeriesQueryAggregator_MAX) 104 query.setDerivative(tspb.TimeSeriesQueryDerivative_NON_NEGATIVE_DERIVATIVE) 105 query.assertSuccess(7, 2) 106 107 // Test queries that return no data. Check with every 108 // aggregator/downsampler/derivative combination. This situation is 109 // particularly prone to nil panics (parts of the query system will not have 110 // data). 111 aggs := []tspb.TimeSeriesQueryAggregator{ 112 tspb.TimeSeriesQueryAggregator_MIN, tspb.TimeSeriesQueryAggregator_MAX, 113 tspb.TimeSeriesQueryAggregator_AVG, tspb.TimeSeriesQueryAggregator_SUM, 114 } 115 derivs := []tspb.TimeSeriesQueryDerivative{ 116 tspb.TimeSeriesQueryDerivative_NONE, tspb.TimeSeriesQueryDerivative_DERIVATIVE, 117 tspb.TimeSeriesQueryDerivative_NON_NEGATIVE_DERIVATIVE, 118 } 119 query = tm.makeQuery("nodata", resolution1ns, 0, 90) 120 for _, downsampler := range aggs { 121 for _, agg := range aggs { 122 for _, deriv := range derivs { 123 query.setDownsampler(downsampler) 124 query.setSourceAggregator(agg) 125 query.setDerivative(deriv) 126 query.assertSuccess(0, 0) 127 } 128 } 129 } 130 131 // Verify querying specific sources, thus excluding other available sources 132 // in the same time period. 133 tm.storeTimeSeriesData(resolution1ns, []tspb.TimeSeriesData{ 134 tsd("test.specificmetric", "source1", 135 tsdp(1, 9999), 136 tsdp(11, 9999), 137 tsdp(21, 9999), 138 tsdp(31, 9999), 139 ), 140 tsd("test.specificmetric", "source2", 141 tsdp(2, 10), 142 tsdp(12, 15), 143 tsdp(22, 25), 144 tsdp(32, 60), 145 ), 146 tsd("test.specificmetric", "source3", 147 tsdp(3, 9999), 148 tsdp(13, 9999), 149 tsdp(23, 9999), 150 tsdp(33, 9999), 151 ), 152 tsd("test.specificmetric", "source4", 153 tsdp(4, 15), 154 tsdp(14, 45), 155 tsdp(24, 60), 156 tsdp(32, 100), 157 ), 158 tsd("test.specificmetric", "source5", 159 tsdp(5, 9999), 160 tsdp(15, 9999), 161 tsdp(25, 9999), 162 tsdp(35, 9999), 163 ), 164 }) 165 166 tm.assertKeyCount(31) 167 tm.assertModelCorrect() 168 169 // Assert querying data from subset of sources. Includes source with no 170 // data. 171 query = tm.makeQuery("test.specificmetric", resolution1ns, 0, 90) 172 query.Sources = []string{"source2", "source4", "source6"} 173 query.assertSuccess(7, 2) 174 175 // Assert querying data over limited range for single source. Regression 176 // test for #4987. 177 query.Sources = []string{"source4", "source5"} 178 query.QueryTimespan = QueryTimespan{ 179 StartNanos: 5, 180 EndNanos: 24, 181 SampleDurationNanos: 1, 182 NowNanos: math.MaxInt64, 183 } 184 query.assertSuccess(4, 2) 185 }) 186 } 187 188 // TestQueryDownsampling validates that query results match the expectation of 189 // the test model. 190 func TestQueryDownsampling(t *testing.T) { 191 defer leaktest.AfterTest(t)() 192 runTestCaseMultipleFormats(t, func(t *testing.T, tm testModelRunner) { 193 // Query with sampleDuration that is too small, expect error. 194 { 195 query := tm.makeQuery("", Resolution10s, 0, 10000) 196 query.SampleDurationNanos = 1 197 query.assertError("was not less") 198 199 // Query with sampleDuration which is not an even multiple of the resolution. 200 query.SampleDurationNanos = Resolution10s.SampleDuration() + 1 201 query.assertError("not a multiple") 202 } 203 204 tm.storeTimeSeriesData(resolution1ns, []tspb.TimeSeriesData{ 205 tsd("test.metric", "source1", 206 tsdp(1, 100), 207 tsdp(5, 500), 208 tsdp(15, 500), 209 tsdp(16, 600), 210 tsdp(17, 700), 211 tsdp(22, 200), 212 tsdp(45, 500), 213 tsdp(46, 600), 214 tsdp(52, 200), 215 ), 216 tsd("test.metric", "source2", 217 tsdp(7, 0), 218 tsdp(7, 700), 219 tsdp(9, 900), 220 tsdp(14, 400), 221 tsdp(18, 800), 222 tsdp(33, 300), 223 tsdp(34, 400), 224 tsdp(56, 600), 225 tsdp(59, 900), 226 ), 227 }) 228 tm.assertKeyCount(9) 229 tm.assertModelCorrect() 230 231 { 232 query := tm.makeQuery("test.metric", resolution1ns, 0, 60) 233 query.SampleDurationNanos = 10 234 query.assertSuccess(6, 2) 235 236 query.Sources = []string{"source1"} 237 query.assertSuccess(5, 1) 238 239 query.Sources = []string{"source2"} 240 query.assertSuccess(4, 1) 241 242 query.Sources = nil 243 query.SampleDurationNanos = 50 244 query.assertSuccess(2, 2) 245 } 246 247 // Query boundaries don't align to downsample period. 248 { 249 query := tm.makeQuery("test.metric", resolution1ns, 15, 35) 250 query.SampleDurationNanos = 10 251 query.assertSuccess(3, 2) 252 } 253 }) 254 } 255 256 // TestInterpolationLimit validates that query results match the expectation of 257 // the test model. 258 func TestInterpolationLimit(t *testing.T) { 259 defer leaktest.AfterTest(t)() 260 runTestCaseMultipleFormats(t, func(t *testing.T, tm testModelRunner) { 261 // Metric with gaps at the edge of a queryable range. 262 // The first source has missing data points at 14 and 19, which can 263 // be interpolated from data points located in nearby slabs. 264 // 5 - [15, 16, 17, 18] - 25 265 tm.storeTimeSeriesData(resolution1ns, []tspb.TimeSeriesData{ 266 tsd("metric.edgegaps", "source1", 267 tsdp(5, 500), 268 tsdp(15, 1500), 269 tsdp(16, 1600), 270 tsdp(17, 1700), 271 tsdp(18, 1800), 272 tsdp(25, 2500), 273 ), 274 tsd("metric.edgegaps", "source2", 275 tsdp(14, 1000), 276 tsdp(15, 1000), 277 tsdp(16, 1000), 278 tsdp(17, 1000), 279 tsdp(18, 1000), 280 tsdp(19, 1000), 281 ), 282 }) 283 tm.assertKeyCount(4) 284 tm.assertModelCorrect() 285 286 { 287 query := tm.makeQuery("metric.edgegaps", resolution1ns, 14, 19) 288 query.assertSuccess(6, 2) 289 query.InterpolationLimitNanos = 10 290 query.assertSuccess(6, 2) 291 query.Sources = []string{"source1", "source2"} 292 query.assertSuccess(6, 2) 293 } 294 295 // Metric with inner gaps which may be effected by the interpolation limit. 296 tm.storeTimeSeriesData(resolution1ns, []tspb.TimeSeriesData{ 297 tsd("metric.innergaps", "source1", 298 tsdp(1, 100), 299 tsdp(2, 200), 300 tsdp(4, 400), 301 tsdp(7, 700), 302 tsdp(10, 1000), 303 ), 304 tsd("metric.innergaps", "source2", 305 tsdp(1, 100), 306 tsdp(2, 100), 307 tsdp(3, 100), 308 tsdp(4, 100), 309 tsdp(5, 100), 310 tsdp(6, 100), 311 tsdp(7, 100), 312 tsdp(8, 100), 313 tsdp(9, 100), 314 ), 315 }) 316 tm.assertKeyCount(7) 317 tm.assertModelCorrect() 318 319 // Interpolation limit 0, 2, 3, and 10. 320 { 321 query := tm.makeQuery("metric.innergaps", resolution1ns, 0, 9) 322 query.assertSuccess(9, 2) 323 query.InterpolationLimitNanos = 2 324 query.assertSuccess(9, 2) 325 query.InterpolationLimitNanos = 3 326 query.assertSuccess(9, 2) 327 query.InterpolationLimitNanos = 10 328 query.assertSuccess(9, 2) 329 } 330 331 // With explicit source list. 332 { 333 query := tm.makeQuery("metric.innergaps", resolution1ns, 0, 9) 334 query.Sources = []string{"source1", "source2"} 335 query.assertSuccess(9, 2) 336 query.InterpolationLimitNanos = 2 337 query.assertSuccess(9, 2) 338 query.InterpolationLimitNanos = 3 339 query.assertSuccess(9, 2) 340 query.InterpolationLimitNanos = 10 341 query.assertSuccess(9, 2) 342 } 343 344 // With derivative. 345 { 346 query := tm.makeQuery("metric.innergaps", resolution1ns, 0, 9) 347 query.Sources = []string{"source1", "source2"} 348 query.setDerivative(tspb.TimeSeriesQueryDerivative_DERIVATIVE) 349 query.assertSuccess(8, 2) 350 query.InterpolationLimitNanos = 2 351 query.assertSuccess(8, 2) 352 query.InterpolationLimitNanos = 3 353 query.assertSuccess(8, 2) 354 query.InterpolationLimitNanos = 10 355 query.assertSuccess(8, 2) 356 } 357 }) 358 } 359 360 func TestQueryWorkerMemoryConstraint(t *testing.T) { 361 defer leaktest.AfterTest(t)() 362 runTestCaseMultipleFormats(t, func(t *testing.T, tm testModelRunner) { 363 generateData := func(dps int64) []tspb.TimeSeriesDatapoint { 364 result := make([]tspb.TimeSeriesDatapoint, 0, dps) 365 var i int64 366 for i = 0; i < dps; i++ { 367 result = append(result, tsdp(time.Duration(i), float64(100*i))) 368 } 369 return result 370 } 371 372 // Store data for a large metric across many keys, so we can test across 373 // many different memory maximums. 374 tm.storeTimeSeriesData(resolution1ns, []tspb.TimeSeriesData{ 375 tsd( 376 "test.metric", 377 "source1", 378 generateData(120)..., 379 ), 380 tsd( 381 "test.metric", 382 "source2", 383 generateData(120)..., 384 ), 385 tsd( 386 "test.metric", 387 "source3", 388 generateData(120)..., 389 ), 390 }) 391 tm.assertKeyCount(36) 392 tm.assertModelCorrect() 393 394 // Track the total maximum memory used for a query with no budget. 395 { 396 // Swap model's memory monitor in order to adjust allocation size. 397 adjustedMon := mon.MakeMonitor( 398 "timeseries-test-worker-adjusted", 399 mon.MemoryResource, 400 nil, 401 nil, 402 1, 403 math.MaxInt64, 404 cluster.MakeTestingClusterSettings(), 405 ) 406 adjustedMon.Start(context.Background(), tm.workerMemMonitor, mon.BoundAccount{}) 407 defer adjustedMon.Stop(context.Background()) 408 409 query := tm.makeQuery("test.metric", resolution1ns, 11, 109) 410 query.workerMemMonitor = &adjustedMon 411 query.InterpolationLimitNanos = 10 412 query.assertSuccess(99, 3) 413 memoryUsed := adjustedMon.MaximumBytes() 414 415 for _, limit := range []int64{ 416 memoryUsed, 417 memoryUsed / 2, 418 memoryUsed / 3, 419 } { 420 // Limit memory in use by model. Reset memory monitor to get new maximum. 421 adjustedMon.Stop(context.Background()) 422 adjustedMon.Start(context.Background(), tm.workerMemMonitor, mon.BoundAccount{}) 423 if adjustedMon.MaximumBytes() != 0 { 424 t.Fatalf("maximum bytes was %d, wanted zero", adjustedMon.MaximumBytes()) 425 } 426 427 query.BudgetBytes = limit 428 query.assertSuccess(99, 3) 429 430 // Expected maximum usage may slightly exceed the budget due to the size of 431 // dataSpan structures which are not accounted for in getMaxTimespan. 432 if a, e := adjustedMon.MaximumBytes(), limit; a > e { 433 t.Fatalf("memory usage for query was %d, exceeded set maximum limit %d", a, e) 434 } 435 436 // As an additional check, ensure that maximum bytes used was within 5% of memory budget; 437 // we want to use as much memory as we can to ensure the fastest possible queries. 438 if a, e := float64(adjustedMon.MaximumBytes()), float64(limit)*0.95; a < e { 439 t.Fatalf("memory usage for query was %f, wanted at least %f", a, e) 440 } 441 } 442 } 443 444 // Verify insufficient memory error bubbles up. 445 { 446 query := tm.makeQuery("test.metric", resolution1ns, 0, 10000) 447 query.BudgetBytes = 1000 448 query.EstimatedSources = 3 449 query.InterpolationLimitNanos = 5 450 query.assertError("insufficient") 451 } 452 }) 453 } 454 455 func TestQueryWorkerMemoryMonitor(t *testing.T) { 456 defer leaktest.AfterTest(t)() 457 runTestCaseMultipleFormats(t, func(t *testing.T, tm testModelRunner) { 458 tm.storeTimeSeriesData(resolution1ns, []tspb.TimeSeriesData{ 459 tsd("test.metric", "", 460 tsdp(1, 100), 461 tsdp(5, 200), 462 tsdp(15, 300), 463 tsdp(16, 400), 464 tsdp(17, 500), 465 tsdp(22, 600), 466 tsdp(52, 900), 467 ), 468 }) 469 tm.assertKeyCount(4) 470 tm.assertModelCorrect() 471 472 // Create a limited bytes monitor. 473 memoryBudget := int64(100 * 1024) 474 limitedMon := mon.MakeMonitorWithLimit( 475 "timeseries-test-limited", 476 mon.MemoryResource, 477 memoryBudget, 478 nil, 479 nil, 480 100, 481 100, 482 cluster.MakeTestingClusterSettings(), 483 ) 484 limitedMon.Start(context.Background(), tm.workerMemMonitor, mon.BoundAccount{}) 485 defer limitedMon.Stop(context.Background()) 486 487 // Assert correctness with no memory pressure. 488 query := tm.makeQuery("test.metric", resolution1ns, 0, 60) 489 query.workerMemMonitor = &limitedMon 490 query.assertSuccess(7, 1) 491 492 // Assert failure with memory pressure. 493 acc := limitedMon.MakeBoundAccount() 494 if err := acc.Grow(context.Background(), memoryBudget-1); err != nil { 495 t.Fatal(err) 496 } 497 498 query.assertError("memory budget exceeded") 499 500 // Assert success again with memory pressure released. 501 acc.Close(context.Background()) 502 query.assertSuccess(7, 1) 503 504 // Start/Stop limited monitor to reset maximum allocation. 505 limitedMon.Stop(context.Background()) 506 limitedMon.Start(context.Background(), tm.workerMemMonitor, mon.BoundAccount{}) 507 508 var ( 509 memStatsBefore runtime.MemStats 510 memStatsAfter runtime.MemStats 511 ) 512 runtime.ReadMemStats(&memStatsBefore) 513 514 query.EndNanos = 10000 515 query.assertSuccess(7, 1) 516 517 runtime.ReadMemStats(&memStatsAfter) 518 t.Logf("total allocations for query: %d\n", memStatsAfter.TotalAlloc-memStatsBefore.TotalAlloc) 519 t.Logf("maximum allocations for query monitor: %d\n", limitedMon.MaximumBytes()) 520 }) 521 } 522 523 // TestQueryBadRequests confirms that the query method returns gentle errors for 524 // obviously bad incoming data. 525 func TestQueryBadRequests(t *testing.T) { 526 defer leaktest.AfterTest(t)() 527 runTestCaseMultipleFormats(t, func(t *testing.T, tm testModelRunner) { 528 // Query with a downsampler that is invalid, expect error. 529 { 530 query := tm.makeQuery("metric.test", resolution1ns, 0, 10000) 531 query.SampleDurationNanos = 10 532 query.setDownsampler((tspb.TimeSeriesQueryAggregator)(999)) 533 query.assertError("unknown time series downsampler") 534 } 535 536 // Query with a aggregator that is invalid, expect error. 537 { 538 query := tm.makeQuery("metric.test", resolution1ns, 0, 10000) 539 query.SampleDurationNanos = 10 540 query.setSourceAggregator((tspb.TimeSeriesQueryAggregator)(999)) 541 query.assertError("unknown time series aggregator") 542 } 543 544 // Query with a downsampler that is invalid, expect no error (default behavior is none). 545 { 546 query := tm.makeQuery("metric.test", resolution1ns, 0, 10000) 547 query.SampleDurationNanos = 10 548 query.setDerivative((tspb.TimeSeriesQueryDerivative)(999)) 549 query.assertSuccess(0, 0) 550 } 551 }) 552 } 553 554 func TestQueryNearCurrentTime(t *testing.T) { 555 defer leaktest.AfterTest(t)() 556 runTestCaseMultipleFormats(t, func(t *testing.T, tm testModelRunner) { 557 tm.storeTimeSeriesData(resolution1ns, []tspb.TimeSeriesData{ 558 tsd("metric.test", "source1", 559 tsdp(1, 100), 560 tsdp(5, 500), 561 tsdp(15, 500), 562 tsdp(16, 600), 563 tsdp(17, 700), 564 tsdp(22, 200), 565 tsdp(45, 500), 566 tsdp(46, 600), 567 tsdp(52, 200), 568 ), 569 tsd("metric.test", "source2", 570 tsdp(7, 0), 571 tsdp(7, 700), 572 tsdp(9, 900), 573 tsdp(14, 400), 574 tsdp(18, 800), 575 tsdp(33, 300), 576 tsdp(34, 400), 577 tsdp(56, 600), 578 tsdp(59, 900), 579 ), 580 }) 581 tm.assertKeyCount(9) 582 tm.assertModelCorrect() 583 584 // All points returned for query with nowNanos in the future. 585 { 586 query := tm.makeQuery("metric.test", resolution1ns, 0, 500) 587 query.NowNanos = 60 588 query.assertSuccess(17, 2) 589 } 590 591 // Test query is disallowed in the future. 592 { 593 query := tm.makeQuery("metric.test", resolution1ns, 20, 500) 594 query.NowNanos = 10 595 query.assertError("cannot query time series in the future") 596 } 597 598 // Test query is truncated so that future datapoints are not queried. 599 { 600 query := tm.makeQuery("metric.test", resolution1ns, 0, 500) 601 query.NowNanos = 30 602 query.assertSuccess(10, 2) 603 } 604 605 // Data points from incomplete periods are not included. 606 { 607 query := tm.makeQuery("metric.test", resolution1ns, 0, 500) 608 query.NowNanos = 59 609 query.assertSuccess(16, 2) 610 } 611 612 // Data points for incomplete periods are not included (with downsampling). 613 { 614 query := tm.makeQuery("metric.test", resolution1ns, 0, 500) 615 query.NowNanos = 60 616 query.SampleDurationNanos = 10 617 query.assertSuccess(6, 2) 618 619 query = tm.makeQuery("metric.test", resolution1ns, 0, 500) 620 query.NowNanos = 59 621 query.SampleDurationNanos = 10 622 query.assertSuccess(5, 2) 623 } 624 }) 625 } 626 627 func TestQueryRollup(t *testing.T) { 628 defer leaktest.AfterTest(t)() 629 630 // Rollups are always columnar, no need to run this test using row format. 631 tm := newTestModelRunner(t) 632 tm.Start() 633 defer tm.Stop() 634 635 tm.storeTimeSeriesData(resolution50ns, []tspb.TimeSeriesData{ 636 tsd("metric.test", "source1", 637 tsdp(1, 100), 638 tsdp(45, 500), 639 tsdp(150, 500), 640 tsdp(165, 600), 641 tsdp(172, 700), 642 tsdp(220, 200), 643 tsdp(230, 200), 644 tsdp(240, 242), 645 tsdp(350, 500), 646 tsdp(520, 199), 647 tsdp(610, 200), 648 tsdp(620, 999), 649 tsdp(750, 200), 650 tsdp(751, 2123), 651 tsdp(921, 500), 652 tsdp(991, 500), 653 tsdp(1001, 1234), 654 tsdp(1002, 234), 655 ), 656 tsd("metric.test", "source2", 657 tsdp(7, 234), 658 tsdp(63, 342), 659 tsdp(74, 342), 660 tsdp(124, 500), 661 tsdp(186, 2345), 662 tsdp(193, 1234), 663 tsdp(220, 200), 664 tsdp(221, 200), 665 tsdp(240, 22342), 666 tsdp(420, 975), 667 tsdp(422, 396), 668 tsdp(498, 6884.74), 669 tsdp(610, 200), 670 tsdp(620, 999), 671 tsdp(750, 200), 672 tsdp(751, 2123), 673 tsdp(854, 9403), 674 tsdp(921, 500), 675 tsdp(991, 500), 676 tsdp(1001, 1234), 677 tsdp(1002, 234), 678 ), 679 }) 680 tm.assertKeyCount(4) 681 tm.assertModelCorrect() 682 683 { 684 query := tm.makeQuery("metric.test", resolution50ns, 100, 1500) 685 query.assertSuccess(13, 2) 686 } 687 688 { 689 query := tm.makeQuery("metric.test", resolution50ns, 450, 850) 690 query.setDownsampler(tspb.TimeSeriesQueryAggregator_MAX) 691 query.setDownsampler(tspb.TimeSeriesQueryAggregator_MIN) 692 query.assertSuccess(5, 2) 693 } 694 695 { 696 query := tm.makeQuery("metric.test", resolution50ns, 100, 1500) 697 query.setDerivative(tspb.TimeSeriesQueryDerivative_DERIVATIVE) 698 query.assertSuccess(13, 2) 699 } 700 }