github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/metrics/rules/rollup_test.go (about) 1 // Copyright (c) 2020 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 rules 22 23 import ( 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/m3db/m3/src/metrics/aggregation" 29 "github.com/m3db/m3/src/metrics/errors" 30 "github.com/m3db/m3/src/metrics/filters" 31 "github.com/m3db/m3/src/metrics/generated/proto/aggregationpb" 32 "github.com/m3db/m3/src/metrics/generated/proto/metricpb" 33 "github.com/m3db/m3/src/metrics/generated/proto/pipelinepb" 34 "github.com/m3db/m3/src/metrics/generated/proto/policypb" 35 "github.com/m3db/m3/src/metrics/generated/proto/rulepb" 36 "github.com/m3db/m3/src/metrics/generated/proto/transformationpb" 37 "github.com/m3db/m3/src/metrics/pipeline" 38 "github.com/m3db/m3/src/metrics/policy" 39 "github.com/m3db/m3/src/metrics/rules/view" 40 "github.com/m3db/m3/src/metrics/transformation" 41 "github.com/m3db/m3/src/query/models" 42 xtime "github.com/m3db/m3/src/x/time" 43 44 "github.com/google/go-cmp/cmp" 45 "github.com/google/go-cmp/cmp/cmpopts" 46 "github.com/stretchr/testify/require" 47 ) 48 49 var ( 50 testRollupRuleSnapshot1V1Proto = &rulepb.RollupRuleSnapshot{ 51 Name: "foo", 52 Tombstoned: false, 53 CutoverNanos: 12345000000, 54 LastUpdatedAtNanos: 12345000000, 55 LastUpdatedBy: "someone", 56 Filter: "tag1:value1 tag2:value2", 57 KeepOriginal: false, 58 Tags: []*metricpb.Tag{}, 59 Targets: []*rulepb.RollupTarget{ 60 { 61 Name: "rName1", 62 Tags: []string{"rtagName1", "rtagName2"}, 63 Policies: []*policypb.Policy{ 64 { 65 StoragePolicy: &policypb.StoragePolicy{ 66 Resolution: policypb.Resolution{ 67 WindowSize: int64(10 * time.Second), 68 Precision: int64(time.Second), 69 }, 70 Retention: policypb.Retention{ 71 Period: int64(24 * time.Hour), 72 }, 73 }, 74 }, 75 }, 76 }, 77 }, 78 } 79 testRollupRuleSnapshot2V1Proto = &rulepb.RollupRuleSnapshot{ 80 Name: "bar", 81 Tombstoned: true, 82 CutoverNanos: 67890000000, 83 LastUpdatedAtNanos: 67890000000, 84 LastUpdatedBy: "someone-else", 85 Filter: "tag3:value3 tag4:value4", 86 KeepOriginal: false, 87 Tags: []*metricpb.Tag{}, 88 Targets: []*rulepb.RollupTarget{ 89 { 90 Name: "rName1", 91 Tags: []string{"rtagName1", "rtagName2"}, 92 Policies: []*policypb.Policy{ 93 { 94 StoragePolicy: &policypb.StoragePolicy{ 95 Resolution: policypb.Resolution{ 96 WindowSize: int64(time.Minute), 97 Precision: int64(time.Minute), 98 }, 99 Retention: policypb.Retention{ 100 Period: int64(24 * time.Hour), 101 }, 102 }, 103 AggregationTypes: []aggregationpb.AggregationType{ 104 aggregationpb.AggregationType_MEAN, 105 }, 106 }, 107 { 108 StoragePolicy: &policypb.StoragePolicy{ 109 Resolution: policypb.Resolution{ 110 WindowSize: int64(5 * time.Minute), 111 Precision: int64(time.Minute), 112 }, 113 Retention: policypb.Retention{ 114 Period: int64(48 * time.Hour), 115 }, 116 }, 117 AggregationTypes: []aggregationpb.AggregationType{ 118 aggregationpb.AggregationType_MEAN, 119 }, 120 }, 121 }, 122 }, 123 }, 124 } 125 testRollupRuleSnapshot3V2Proto = &rulepb.RollupRuleSnapshot{ 126 Name: "foo", 127 Tombstoned: false, 128 CutoverNanos: 12345000000, 129 LastUpdatedAtNanos: 12345000000, 130 LastUpdatedBy: "someone", 131 Filter: "tag1:value1 tag2:value2", 132 KeepOriginal: false, 133 Tags: []*metricpb.Tag{}, 134 TargetsV2: []*rulepb.RollupTargetV2{ 135 { 136 Pipeline: &pipelinepb.Pipeline{ 137 Ops: []pipelinepb.PipelineOp{ 138 { 139 Type: pipelinepb.PipelineOp_AGGREGATION, 140 Aggregation: &pipelinepb.AggregationOp{ 141 Type: aggregationpb.AggregationType_SUM, 142 }, 143 }, 144 { 145 Type: pipelinepb.PipelineOp_TRANSFORMATION, 146 Transformation: &pipelinepb.TransformationOp{ 147 Type: transformationpb.TransformationType_ABSOLUTE, 148 }, 149 }, 150 { 151 Type: pipelinepb.PipelineOp_ROLLUP, 152 Rollup: &pipelinepb.RollupOp{ 153 NewName: "testRollupOp", 154 Tags: []string{"testTag1", "testTag2"}, 155 AggregationTypes: []aggregationpb.AggregationType{ 156 aggregationpb.AggregationType_MIN, 157 aggregationpb.AggregationType_MAX, 158 }, 159 }, 160 }, 161 }, 162 }, 163 StoragePolicies: []*policypb.StoragePolicy{ 164 { 165 Resolution: policypb.Resolution{ 166 WindowSize: 10 * time.Second.Nanoseconds(), 167 Precision: time.Second.Nanoseconds(), 168 }, 169 Retention: policypb.Retention{ 170 Period: 24 * time.Hour.Nanoseconds(), 171 }, 172 }, 173 { 174 Resolution: policypb.Resolution{ 175 WindowSize: time.Minute.Nanoseconds(), 176 Precision: time.Minute.Nanoseconds(), 177 }, 178 Retention: policypb.Retention{ 179 Period: 720 * time.Hour.Nanoseconds(), 180 }, 181 }, 182 { 183 Resolution: policypb.Resolution{ 184 WindowSize: time.Hour.Nanoseconds(), 185 Precision: time.Hour.Nanoseconds(), 186 }, 187 Retention: policypb.Retention{ 188 Period: 365 * 24 * time.Hour.Nanoseconds(), 189 }, 190 }, 191 }, 192 }, 193 { 194 Pipeline: &pipelinepb.Pipeline{ 195 Ops: []pipelinepb.PipelineOp{ 196 { 197 Type: pipelinepb.PipelineOp_TRANSFORMATION, 198 Transformation: &pipelinepb.TransformationOp{ 199 Type: transformationpb.TransformationType_PERSECOND, 200 }, 201 }, 202 { 203 Type: pipelinepb.PipelineOp_ROLLUP, 204 Rollup: &pipelinepb.RollupOp{ 205 NewName: "testRollupOp2", 206 Tags: []string{"testTag3", "testTag4"}, 207 }, 208 }, 209 }, 210 }, 211 StoragePolicies: []*policypb.StoragePolicy{ 212 { 213 Resolution: policypb.Resolution{ 214 WindowSize: time.Minute.Nanoseconds(), 215 Precision: time.Minute.Nanoseconds(), 216 }, 217 Retention: policypb.Retention{ 218 Period: 720 * time.Hour.Nanoseconds(), 219 }, 220 }, 221 }, 222 }, 223 }, 224 } 225 testRollupRuleSnapshot4V2Proto = &rulepb.RollupRuleSnapshot{ 226 Name: "bar", 227 Tombstoned: true, 228 CutoverNanos: 67890000000, 229 LastUpdatedAtNanos: 67890000000, 230 LastUpdatedBy: "someone-else", 231 Filter: "tag3:value3 tag4:value4", 232 KeepOriginal: true, 233 Tags: []*metricpb.Tag{}, 234 TargetsV2: []*rulepb.RollupTargetV2{ 235 { 236 Pipeline: &pipelinepb.Pipeline{ 237 Ops: []pipelinepb.PipelineOp{ 238 { 239 Type: pipelinepb.PipelineOp_ROLLUP, 240 Rollup: &pipelinepb.RollupOp{ 241 NewName: "testRollupOp2", 242 Tags: []string{"testTag3", "testTag4"}, 243 AggregationTypes: []aggregationpb.AggregationType{ 244 aggregationpb.AggregationType_LAST, 245 }, 246 }, 247 }, 248 }, 249 }, 250 StoragePolicies: []*policypb.StoragePolicy{ 251 { 252 Resolution: policypb.Resolution{ 253 WindowSize: 10 * time.Minute.Nanoseconds(), 254 Precision: time.Minute.Nanoseconds(), 255 }, 256 Retention: policypb.Retention{ 257 Period: 1800 * time.Hour.Nanoseconds(), 258 }, 259 }, 260 }, 261 }, 262 }, 263 } 264 testRollupRule1V1Proto = &rulepb.RollupRule{ 265 Uuid: "12669817-13ae-40e6-ba2f-33087b262c68", 266 Snapshots: []*rulepb.RollupRuleSnapshot{ 267 testRollupRuleSnapshot1V1Proto, 268 testRollupRuleSnapshot2V1Proto, 269 }, 270 } 271 testRollupRule2V2Proto = &rulepb.RollupRule{ 272 Uuid: "12669817-13ae-40e6-ba2f-33087b262c68", 273 Snapshots: []*rulepb.RollupRuleSnapshot{ 274 testRollupRuleSnapshot3V2Proto, 275 testRollupRuleSnapshot4V2Proto, 276 }, 277 } 278 rr1, rr1err = pipeline.NewRollupOp( 279 pipeline.GroupByRollupType, 280 "rName1", 281 []string{"rtagName1", "rtagName2"}, 282 aggregation.DefaultID, 283 ) 284 rr2, rr2err = pipeline.NewRollupOp( 285 pipeline.GroupByRollupType, 286 "rName1", 287 []string{"rtagName1", "rtagName2"}, 288 aggregation.MustCompressTypes(aggregation.Mean), 289 ) 290 rr3, rr3err = pipeline.NewRollupOp( 291 pipeline.GroupByRollupType, 292 "testRollupOp", 293 []string{"testTag1", "testTag2"}, 294 aggregation.MustCompressTypes(aggregation.Min, aggregation.Max), 295 ) 296 rr4, rr4err = pipeline.NewRollupOp( 297 pipeline.GroupByRollupType, 298 "testRollupOp2", 299 []string{"testTag3", "testTag4"}, 300 aggregation.DefaultID, 301 ) 302 rr5, rr5err = pipeline.NewRollupOp( 303 pipeline.GroupByRollupType, 304 "testRollupOp2", 305 []string{"testTag3", "testTag4"}, 306 aggregation.MustCompressTypes(aggregation.Last), 307 ) 308 testRollupRuleSnapshot1 = &rollupRuleSnapshot{ 309 name: "foo", 310 tombstoned: false, 311 cutoverNanos: 12345000000, 312 rawFilter: "tag1:value1 tag2:value2", 313 keepOriginal: false, 314 tags: []models.Tag{}, 315 targets: []rollupTarget{ 316 { 317 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 318 { 319 Type: pipeline.RollupOpType, 320 Rollup: rr1, 321 }, 322 }), 323 StoragePolicies: policy.StoragePolicies{ 324 policy.NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 325 }, 326 }, 327 }, 328 lastUpdatedAtNanos: 12345000000, 329 lastUpdatedBy: "someone", 330 } 331 testRollupRuleSnapshot2 = &rollupRuleSnapshot{ 332 name: "bar", 333 tombstoned: true, 334 cutoverNanos: 67890000000, 335 rawFilter: "tag3:value3 tag4:value4", 336 keepOriginal: false, 337 tags: []models.Tag{}, 338 targets: []rollupTarget{ 339 { 340 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 341 { 342 Type: pipeline.RollupOpType, 343 Rollup: rr2, 344 }, 345 }), 346 StoragePolicies: policy.StoragePolicies{ 347 policy.NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 348 policy.NewStoragePolicy(5*time.Minute, xtime.Minute, 48*time.Hour), 349 }, 350 }, 351 }, 352 lastUpdatedAtNanos: 67890000000, 353 lastUpdatedBy: "someone-else", 354 } 355 testRollupRuleSnapshot3 = &rollupRuleSnapshot{ 356 name: "foo", 357 tombstoned: false, 358 cutoverNanos: 12345000000, 359 rawFilter: "tag1:value1 tag2:value2", 360 keepOriginal: false, 361 tags: []models.Tag{}, 362 targets: []rollupTarget{ 363 { 364 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 365 { 366 Type: pipeline.AggregationOpType, 367 Aggregation: pipeline.AggregationOp{ 368 Type: aggregation.Sum, 369 }, 370 }, 371 { 372 Type: pipeline.TransformationOpType, 373 Transformation: pipeline.TransformationOp{ 374 Type: transformation.Absolute, 375 }, 376 }, 377 { 378 Type: pipeline.RollupOpType, 379 Rollup: rr3, 380 }, 381 }), 382 StoragePolicies: policy.StoragePolicies{ 383 policy.NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 384 policy.NewStoragePolicy(time.Minute, xtime.Minute, 720*time.Hour), 385 policy.NewStoragePolicy(time.Hour, xtime.Hour, 365*24*time.Hour), 386 }, 387 }, 388 { 389 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 390 { 391 Type: pipeline.TransformationOpType, 392 Transformation: pipeline.TransformationOp{ 393 Type: transformation.PerSecond, 394 }, 395 }, 396 { 397 Type: pipeline.RollupOpType, 398 Rollup: rr4, 399 }, 400 }), 401 StoragePolicies: policy.StoragePolicies{ 402 policy.NewStoragePolicy(time.Minute, xtime.Minute, 720*time.Hour), 403 }, 404 }, 405 }, 406 lastUpdatedAtNanos: 12345000000, 407 lastUpdatedBy: "someone", 408 } 409 testRollupRuleSnapshot4 = &rollupRuleSnapshot{ 410 name: "bar", 411 tombstoned: true, 412 cutoverNanos: 67890000000, 413 rawFilter: "tag3:value3 tag4:value4", 414 keepOriginal: true, 415 tags: []models.Tag{}, 416 targets: []rollupTarget{ 417 { 418 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 419 { 420 Type: pipeline.RollupOpType, 421 Rollup: rr5, 422 }, 423 }), 424 StoragePolicies: policy.StoragePolicies{ 425 policy.NewStoragePolicy(10*time.Minute, xtime.Minute, 1800*time.Hour), 426 }, 427 }, 428 }, 429 lastUpdatedAtNanos: 67890000000, 430 lastUpdatedBy: "someone-else", 431 } 432 testRollupRule1 = &rollupRule{ 433 uuid: "12669817-13ae-40e6-ba2f-33087b262c68", 434 snapshots: []*rollupRuleSnapshot{ 435 testRollupRuleSnapshot1, 436 testRollupRuleSnapshot2, 437 }, 438 } 439 testRollupRule2 = &rollupRule{ 440 uuid: "12669817-13ae-40e6-ba2f-33087b262c68", 441 snapshots: []*rollupRuleSnapshot{ 442 testRollupRuleSnapshot3, 443 testRollupRuleSnapshot4, 444 }, 445 } 446 testRollupRuleSnapshotCmpOpts = []cmp.Option{ 447 cmp.AllowUnexported(rollupRuleSnapshot{}), 448 cmpopts.IgnoreInterfaces(struct{ filters.TagsFilter }{}), 449 } 450 testRollupRuleCmpOpts = []cmp.Option{ 451 cmp.AllowUnexported(rollupRule{}), 452 cmp.AllowUnexported(rollupRuleSnapshot{}), 453 cmpopts.IgnoreInterfaces(struct{ filters.TagsFilter }{}), 454 } 455 ) 456 457 func TestErrCheck(t *testing.T) { 458 require.NoError(t, rr1err) 459 require.NoError(t, rr2err) 460 require.NoError(t, rr3err) 461 require.NoError(t, rr4err) 462 require.NoError(t, rr5err) 463 } 464 465 func TestNewRollupRuleSnapshotFromProtoNilProto(t *testing.T) { 466 _, err := newRollupRuleSnapshotFromProto(nil, testTagsFilterOptions()) 467 require.Equal(t, errNilRollupRuleSnapshotProto, err) 468 } 469 470 func TestNewRollupRuleSnapshotFromV1ProtoInvalidProto(t *testing.T) { 471 proto := &rulepb.RollupRuleSnapshot{ 472 Targets: []*rulepb.RollupTarget{ 473 { 474 Name: "rName1", 475 Tags: []string{"rtagName1", "rtagName2"}, 476 Policies: []*policypb.Policy{ 477 {}, 478 }, 479 }, 480 }, 481 } 482 _, err := newRollupRuleSnapshotFromProto(proto, testTagsFilterOptions()) 483 require.Error(t, err) 484 } 485 486 func TestNewRollupRuleSnapshotFromV1Proto(t *testing.T) { 487 filterOpts := testTagsFilterOptions() 488 inputs := []*rulepb.RollupRuleSnapshot{ 489 testRollupRuleSnapshot1V1Proto, 490 testRollupRuleSnapshot2V1Proto, 491 } 492 expected := []*rollupRuleSnapshot{ 493 testRollupRuleSnapshot1, 494 testRollupRuleSnapshot2, 495 } 496 for i, input := range inputs { 497 res, err := newRollupRuleSnapshotFromProto(input, filterOpts) 498 require.NoError(t, err) 499 require.True(t, cmp.Equal(expected[i], res, testRollupRuleSnapshotCmpOpts...)) 500 require.NotNil(t, res.filter) 501 } 502 } 503 504 func TestNewRollupRuleSnapshotFromV2ProtoInvalidProto(t *testing.T) { 505 filterOpts := testTagsFilterOptions() 506 proto := &rulepb.RollupRuleSnapshot{ 507 TargetsV2: []*rulepb.RollupTargetV2{ 508 { 509 Pipeline: &pipelinepb.Pipeline{ 510 Ops: []pipelinepb.PipelineOp{ 511 { 512 Type: pipelinepb.PipelineOp_TRANSFORMATION, 513 Transformation: &pipelinepb.TransformationOp{ 514 Type: transformationpb.TransformationType_UNKNOWN, 515 }, 516 }, 517 }, 518 }, 519 StoragePolicies: []*policypb.StoragePolicy{ 520 { 521 Resolution: policypb.Resolution{ 522 WindowSize: 10 * time.Minute.Nanoseconds(), 523 Precision: time.Minute.Nanoseconds(), 524 }, 525 Retention: policypb.Retention{ 526 Period: 1800 * time.Hour.Nanoseconds(), 527 }, 528 }, 529 }, 530 }, 531 }, 532 } 533 _, err := newRollupRuleSnapshotFromProto(proto, filterOpts) 534 require.Error(t, err) 535 } 536 537 func TestNewRollupRuleSnapshotFromV2Proto(t *testing.T) { 538 filterOpts := testTagsFilterOptions() 539 inputs := []*rulepb.RollupRuleSnapshot{ 540 testRollupRuleSnapshot3V2Proto, 541 testRollupRuleSnapshot4V2Proto, 542 } 543 expected := []*rollupRuleSnapshot{ 544 testRollupRuleSnapshot3, 545 testRollupRuleSnapshot4, 546 } 547 for i, input := range inputs { 548 res, err := newRollupRuleSnapshotFromProto(input, filterOpts) 549 require.NoError(t, err) 550 require.True(t, cmp.Equal(expected[i], res, testRollupRuleSnapshotCmpOpts...)) 551 require.NotNil(t, res.filter) 552 } 553 } 554 555 func TestNewRollupRuleSnapshotFromProtoTombstoned(t *testing.T) { 556 filterOpts := testTagsFilterOptions() 557 input := &rulepb.RollupRuleSnapshot{ 558 Name: "foo", 559 Tombstoned: true, 560 CutoverNanos: 12345000000, 561 LastUpdatedAtNanos: 12345000000, 562 LastUpdatedBy: "someone", 563 Filter: "tag1:value1 tag2:value2", 564 KeepOriginal: false, 565 } 566 res, err := newRollupRuleSnapshotFromProto(input, filterOpts) 567 require.NoError(t, err) 568 569 expected := &rollupRuleSnapshot{ 570 name: "foo", 571 tombstoned: true, 572 cutoverNanos: 12345000000, 573 rawFilter: "tag1:value1 tag2:value2", 574 lastUpdatedAtNanos: 12345000000, 575 lastUpdatedBy: "someone", 576 keepOriginal: false, 577 tags: []models.Tag{}, 578 } 579 require.True(t, cmp.Equal(expected, res, testRollupRuleSnapshotCmpOpts...)) 580 require.NotNil(t, res.filter) 581 } 582 583 func TestNewRollupRuleSnapshotNoRollupTargets(t *testing.T) { 584 proto := &rulepb.RollupRuleSnapshot{} 585 _, err := newRollupRuleSnapshotFromProto(proto, testTagsFilterOptions()) 586 require.Equal(t, errNoRollupTargetsInRollupRuleSnapshot, err) 587 } 588 589 func TestNewRollupRuleSnapshotFromFields(t *testing.T) { 590 res, err := newRollupRuleSnapshotFromFields( 591 testRollupRuleSnapshot3.name, 592 testRollupRuleSnapshot3.cutoverNanos, 593 testRollupRuleSnapshot3.rawFilter, 594 testRollupRuleSnapshot3.targets, 595 testRollupRuleSnapshot3.filter, 596 testRollupRuleSnapshot3.lastUpdatedAtNanos, 597 testRollupRuleSnapshot3.lastUpdatedBy, 598 false, 599 []models.Tag{}, 600 ) 601 require.NoError(t, err) 602 require.True(t, cmp.Equal(testRollupRuleSnapshot3, res, testRollupRuleSnapshotCmpOpts...)) 603 } 604 605 func TestNewRollupRuleSnapshotFromFieldsValidationError(t *testing.T) { 606 badFilters := []string{ 607 "tag3:", 608 "tag3:*a*b*c*d", 609 "ab[cd", 610 } 611 612 for _, f := range badFilters { 613 _, err := newRollupRuleSnapshotFromFields( 614 "bar", 615 12345000000, 616 f, 617 nil, 618 nil, 619 1234, 620 "test_user", 621 false, 622 nil, 623 ) 624 require.Error(t, err) 625 _, ok := err.(errors.ValidationError) 626 require.True(t, ok) 627 } 628 } 629 630 func TestRollupRuleSnapshotProto(t *testing.T) { 631 snapshots := []*rollupRuleSnapshot{ 632 testRollupRuleSnapshot3, 633 testRollupRuleSnapshot4, 634 } 635 expected := []*rulepb.RollupRuleSnapshot{ 636 testRollupRuleSnapshot3V2Proto, 637 testRollupRuleSnapshot4V2Proto, 638 } 639 for i, snapshot := range snapshots { 640 proto, err := snapshot.proto() 641 require.NoError(t, err) 642 require.Equal(t, expected[i], proto) 643 } 644 } 645 646 func TestNewRollupRuleFromProtoNilProto(t *testing.T) { 647 _, err := newRollupRuleFromProto(nil, testTagsFilterOptions()) 648 require.Equal(t, errNilRollupRuleProto, err) 649 } 650 651 func TestNewRollupRuleFromProtoValidProto(t *testing.T) { 652 filterOpts := testTagsFilterOptions() 653 inputs := []*rulepb.RollupRule{ 654 testRollupRule1V1Proto, 655 testRollupRule2V2Proto, 656 } 657 expected := []*rollupRule{ 658 testRollupRule1, 659 testRollupRule2, 660 } 661 for i, input := range inputs { 662 res, err := newRollupRuleFromProto(input, filterOpts) 663 require.NoError(t, err) 664 require.True(t, cmp.Equal(expected[i], res, testRollupRuleCmpOpts...)) 665 } 666 } 667 668 func TestRollupRuleClone(t *testing.T) { 669 inputs := []*rollupRule{ 670 testRollupRule1, 671 testRollupRule2, 672 } 673 for _, input := range inputs { 674 cloned := input.clone() 675 require.True(t, cmp.Equal(&cloned, input, testRollupRuleCmpOpts...)) 676 677 // Asserting that modifying the clone doesn't modify the original rollup rule. 678 cloned2 := input.clone() 679 require.True(t, cmp.Equal(&cloned2, input, testRollupRuleCmpOpts...)) 680 cloned2.snapshots[0].tombstoned = true 681 require.False(t, cmp.Equal(&cloned2, input, testRollupRuleCmpOpts...)) 682 require.True(t, cmp.Equal(&cloned, input, testRollupRuleCmpOpts...)) 683 } 684 } 685 686 func TestRollupRuleProto(t *testing.T) { 687 inputs := []*rollupRule{ 688 testRollupRule2, 689 } 690 expected := []*rulepb.RollupRule{ 691 testRollupRule2V2Proto, 692 } 693 for i, input := range inputs { 694 res, err := input.proto() 695 require.NoError(t, err) 696 require.Equal(t, expected[i], res) 697 } 698 } 699 700 func TestRollupRuleActiveSnapshotNotFound(t *testing.T) { 701 require.Nil(t, testRollupRule2.activeSnapshot(0)) 702 } 703 704 func TestRollupRuleActiveSnapshotFound(t *testing.T) { 705 require.Equal(t, testRollupRule2.snapshots[1], testRollupRule2.activeSnapshot(100000000000)) 706 } 707 708 func TestRollupRuleActiveRuleNotFound(t *testing.T) { 709 require.Equal(t, testRollupRule2, testRollupRule2.activeRule(0)) 710 } 711 712 func TestRollupRuleActiveRuleFound(t *testing.T) { 713 expected := &rollupRule{ 714 uuid: testRollupRule2.uuid, 715 snapshots: testRollupRule2.snapshots[1:], 716 } 717 require.Equal(t, expected, testRollupRule2.activeRule(100000000000)) 718 } 719 720 func TestRollupNameNoSnapshot(t *testing.T) { 721 rr := rollupRule{ 722 uuid: "blah", 723 snapshots: []*rollupRuleSnapshot{}, 724 } 725 _, err := rr.name() 726 require.Equal(t, errNoRuleSnapshots, err) 727 } 728 729 func TestRollupTombstonedNoSnapshot(t *testing.T) { 730 rr := rollupRule{ 731 uuid: "blah", 732 snapshots: []*rollupRuleSnapshot{}, 733 } 734 require.True(t, rr.tombstoned()) 735 } 736 737 func TestRollupTombstoned(t *testing.T) { 738 require.True(t, testRollupRule2.tombstoned()) 739 } 740 741 func TestRollupRuleMarkTombstoned(t *testing.T) { 742 proto := &rulepb.RollupRule{ 743 Uuid: "12669817-13ae-40e6-ba2f-33087b262c68", 744 Snapshots: []*rulepb.RollupRuleSnapshot{ 745 testRollupRuleSnapshot3V2Proto, 746 }, 747 } 748 rr, err := newRollupRuleFromProto(proto, testTagsFilterOptions()) 749 require.NoError(t, err) 750 751 meta := UpdateMetadata{ 752 cutoverNanos: 67890000000, 753 updatedAtNanos: 10000, 754 updatedBy: "john", 755 } 756 require.NoError(t, rr.markTombstoned(meta)) 757 require.Equal(t, 2, len(rr.snapshots)) 758 require.True(t, cmp.Equal(testRollupRuleSnapshot3, rr.snapshots[0], testRollupRuleSnapshotCmpOpts...)) 759 760 expected := &rollupRuleSnapshot{ 761 name: "foo", 762 tombstoned: true, 763 cutoverNanos: 67890000000, 764 rawFilter: "tag1:value1 tag2:value2", 765 lastUpdatedAtNanos: 10000, 766 lastUpdatedBy: "john", 767 keepOriginal: false, 768 tags: []models.Tag{}, 769 } 770 require.True(t, cmp.Equal(expected, rr.snapshots[1], testRollupRuleSnapshotCmpOpts...)) 771 } 772 773 func TestRollupRuleMarkTombstonedNoSnapshots(t *testing.T) { 774 rr := &rollupRule{} 775 require.Error(t, rr.markTombstoned(UpdateMetadata{})) 776 } 777 778 func TestRollupRuleMarkTombstonedAlreadyTombstoned(t *testing.T) { 779 err := testRollupRule2.markTombstoned(UpdateMetadata{}) 780 require.Error(t, err) 781 require.True(t, strings.Contains(err.Error(), "bar is already tombstoned")) 782 } 783 784 func TestRollupRuleRollupRuleView(t *testing.T) { 785 res, err := testRollupRule2.rollupRuleView(1) 786 require.NoError(t, err) 787 rr1, err = pipeline.NewRollupOp( 788 pipeline.GroupByRollupType, 789 "testRollupOp2", 790 []string{"testTag3", "testTag4"}, 791 aggregation.MustCompressTypes(aggregation.Last), 792 ) 793 require.NoError(t, err) 794 expected := view.RollupRule{ 795 ID: "12669817-13ae-40e6-ba2f-33087b262c68", 796 Name: "bar", 797 Tombstoned: true, 798 CutoverMillis: 67890, 799 Filter: "tag3:value3 tag4:value4", 800 KeepOriginal: true, 801 Targets: []view.RollupTarget{ 802 { 803 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 804 { 805 Type: pipeline.RollupOpType, 806 Rollup: rr1, 807 }, 808 }), 809 StoragePolicies: policy.StoragePolicies{ 810 policy.NewStoragePolicy(10*time.Minute, xtime.Minute, 1800*time.Hour), 811 }, 812 }, 813 }, 814 LastUpdatedAtMillis: 67890, 815 LastUpdatedBy: "someone-else", 816 Tags: []models.Tag{}, 817 } 818 require.Equal(t, expected, res) 819 } 820 821 func TestNewRollupRuleViewError(t *testing.T) { 822 badIndices := []int{-2, 2, 30} 823 for _, i := range badIndices { 824 _, err := testRollupRule2.rollupRuleView(i) 825 require.Equal(t, errRollupRuleSnapshotIndexOutOfRange, err) 826 } 827 } 828 829 func TestNewRollupRuleHistory(t *testing.T) { 830 history, err := testRollupRule2.history() 831 require.NoError(t, err) 832 833 rr1, err = pipeline.NewRollupOp( 834 pipeline.GroupByRollupType, 835 "testRollupOp2", 836 []string{"testTag3", "testTag4"}, 837 aggregation.MustCompressTypes(aggregation.Last), 838 ) 839 require.NoError(t, err) 840 rr2, err = pipeline.NewRollupOp( 841 pipeline.GroupByRollupType, 842 "testRollupOp", 843 []string{"testTag1", "testTag2"}, 844 aggregation.MustCompressTypes(aggregation.Min, aggregation.Max), 845 ) 846 require.NoError(t, err) 847 rr3, err = pipeline.NewRollupOp( 848 pipeline.GroupByRollupType, 849 "testRollupOp2", 850 []string{"testTag3", "testTag4"}, 851 aggregation.DefaultID, 852 ) 853 require.NoError(t, err) 854 855 expected := []view.RollupRule{ 856 { 857 ID: "12669817-13ae-40e6-ba2f-33087b262c68", 858 Name: "bar", 859 Tombstoned: true, 860 CutoverMillis: 67890, 861 Filter: "tag3:value3 tag4:value4", 862 KeepOriginal: true, 863 Targets: []view.RollupTarget{ 864 { 865 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 866 { 867 Type: pipeline.RollupOpType, 868 Rollup: rr1, 869 }, 870 }), 871 StoragePolicies: policy.StoragePolicies{ 872 policy.NewStoragePolicy(10*time.Minute, xtime.Minute, 1800*time.Hour), 873 }, 874 }, 875 }, 876 LastUpdatedAtMillis: 67890, 877 LastUpdatedBy: "someone-else", 878 Tags: []models.Tag{}, 879 }, 880 { 881 ID: "12669817-13ae-40e6-ba2f-33087b262c68", 882 Name: "foo", 883 Tombstoned: false, 884 CutoverMillis: 12345, 885 Filter: "tag1:value1 tag2:value2", 886 Targets: []view.RollupTarget{ 887 { 888 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 889 { 890 Type: pipeline.AggregationOpType, 891 Aggregation: pipeline.AggregationOp{ 892 Type: aggregation.Sum, 893 }, 894 }, 895 { 896 Type: pipeline.TransformationOpType, 897 Transformation: pipeline.TransformationOp{ 898 Type: transformation.Absolute, 899 }, 900 }, 901 { 902 Type: pipeline.RollupOpType, 903 Rollup: rr2, 904 }, 905 }), 906 StoragePolicies: policy.StoragePolicies{ 907 policy.NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 908 policy.NewStoragePolicy(time.Minute, xtime.Minute, 720*time.Hour), 909 policy.NewStoragePolicy(time.Hour, xtime.Hour, 365*24*time.Hour), 910 }, 911 }, 912 { 913 Pipeline: pipeline.NewPipeline([]pipeline.OpUnion{ 914 { 915 Type: pipeline.TransformationOpType, 916 Transformation: pipeline.TransformationOp{ 917 Type: transformation.PerSecond, 918 }, 919 }, 920 { 921 Type: pipeline.RollupOpType, 922 Rollup: rr3, 923 }, 924 }), 925 StoragePolicies: policy.StoragePolicies{ 926 policy.NewStoragePolicy(time.Minute, xtime.Minute, 720*time.Hour), 927 }, 928 }, 929 }, 930 LastUpdatedAtMillis: 12345, 931 LastUpdatedBy: "someone", 932 Tags: []models.Tag{}, 933 }, 934 } 935 require.Equal(t, expected, history) 936 }