github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/cmd/services/m3coordinator/ingest/write_test.go (about) 1 // Copyright (c) 2019 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 ingest 22 23 import ( 24 "context" 25 "fmt" 26 "testing" 27 "time" 28 29 "github.com/m3db/m3/src/cmd/services/m3coordinator/downsample" 30 "github.com/m3db/m3/src/dbnode/client" 31 "github.com/m3db/m3/src/metrics/aggregation" 32 "github.com/m3db/m3/src/metrics/policy" 33 "github.com/m3db/m3/src/query/models" 34 "github.com/m3db/m3/src/query/storage" 35 "github.com/m3db/m3/src/query/storage/m3" 36 testm3 "github.com/m3db/m3/src/query/test/m3" 37 "github.com/m3db/m3/src/query/ts" 38 xerrors "github.com/m3db/m3/src/x/errors" 39 "github.com/m3db/m3/src/x/ident" 40 "github.com/m3db/m3/src/x/instrument" 41 xsync "github.com/m3db/m3/src/x/sync" 42 xtime "github.com/m3db/m3/src/x/time" 43 44 "github.com/golang/mock/gomock" 45 "github.com/stretchr/testify/require" 46 ) 47 48 var ( 49 // Created by init(). 50 testWorkerPool xsync.PooledWorkerPool 51 52 source = ts.SourceTypePrometheus 53 54 testTags1 = models.NewTags(3, nil).AddTags( 55 []models.Tag{ 56 { 57 Name: []byte("test_1_key_1"), 58 Value: []byte("test_1_value_1"), 59 }, 60 { 61 Name: []byte("test_1_key_2"), 62 Value: []byte("test_1_value_2"), 63 }, 64 { 65 Name: []byte("test_1_key_3"), 66 Value: []byte("test_1_value_3"), 67 }, 68 }, 69 ) 70 testTags2 = models.NewTags(3, nil).AddTags( 71 []models.Tag{ 72 { 73 Name: []byte("test_2_key_1"), 74 Value: []byte("test_2_value_1"), 75 }, 76 { 77 Name: []byte("test_2_key_2"), 78 Value: []byte("test_2_value_2"), 79 }, 80 { 81 Name: []byte("test_2_key_3"), 82 Value: []byte("test_2_value_3"), 83 }, 84 }, 85 ) 86 testBadTags = models.NewTags(3, nil).AddTags([]models.Tag{ 87 { 88 Name: []byte("standard_tag"), 89 Value: []byte("standard_tag_value"), 90 }, 91 { 92 Name: []byte("duplicate_tag"), 93 Value: []byte("duplicate_tag_value0"), 94 }, 95 { 96 Name: []byte("duplicate_tag"), 97 Value: []byte("duplicate_tag_value1"), 98 }, 99 }) 100 101 testDatapoints1 = []ts.Datapoint{ 102 { 103 Timestamp: xtime.UnixNano(0), 104 Value: 0, 105 }, 106 { 107 Timestamp: xtime.UnixNano(1), 108 Value: 1, 109 }, 110 { 111 Timestamp: xtime.UnixNano(2), 112 Value: 2, 113 }, 114 } 115 testDatapoints2 = []ts.Datapoint{ 116 { 117 Timestamp: xtime.UnixNano(3), 118 Value: 3, 119 }, 120 { 121 Timestamp: xtime.UnixNano(4), 122 Value: 4, 123 }, 124 { 125 Timestamp: xtime.UnixNano(5), 126 Value: 5, 127 }, 128 } 129 130 testAnnotation1 = []byte("first") 131 testAnnotation2 = []byte("second") 132 133 testAttributesGauge = ts.SeriesAttributes{ 134 M3Type: ts.M3MetricTypeGauge, 135 } 136 testAttributesCounter = ts.SeriesAttributes{ 137 M3Type: ts.M3MetricTypeCounter, 138 } 139 testAttributesTimer = ts.SeriesAttributes{ 140 M3Type: ts.M3MetricTypeTimer, 141 } 142 143 testEntries = []testIterEntry{ 144 {tags: testTags1, datapoints: testDatapoints1, attributes: testAttributesGauge, annotation: testAnnotation1}, 145 {tags: testTags2, datapoints: testDatapoints2, attributes: testAttributesGauge, annotation: testAnnotation2}, 146 } 147 148 testEntries2 = []testIterEntry{ 149 {tags: testTags1, datapoints: testDatapoints1, attributes: testAttributesCounter, annotation: testAnnotation1}, 150 {tags: testTags2, datapoints: testDatapoints2, attributes: testAttributesTimer, annotation: testAnnotation2}, 151 } 152 153 defaultOverride = WriteOptions{} 154 155 zeroDownsamplerAppenderOpts = downsample.SampleAppenderOptions{} 156 ) 157 158 type testIter struct { 159 idx int 160 entries []testIterEntry 161 metadatas []ts.Metadata 162 } 163 164 type testIterEntry struct { 165 tags models.Tags 166 datapoints []ts.Datapoint 167 annotation []byte 168 attributes ts.SeriesAttributes 169 } 170 171 func newTestIter(entries []testIterEntry) *testIter { 172 return &testIter{ 173 idx: -1, 174 entries: entries, 175 metadatas: make([]ts.Metadata, 10), 176 } 177 } 178 179 func (i *testIter) Next() bool { 180 i.idx++ 181 return i.idx < len(i.entries) 182 } 183 184 func (i *testIter) Current() IterValue { 185 if len(i.entries) == 0 || i.idx < 0 || i.idx >= len(i.entries) { 186 return IterValue{ 187 Tags: models.EmptyTags(), 188 Attributes: ts.DefaultSeriesAttributes(), 189 } 190 } 191 192 curr := i.entries[i.idx] 193 value := IterValue{ 194 Tags: curr.tags, 195 Datapoints: curr.datapoints, 196 Attributes: curr.attributes, 197 Unit: xtime.Second, 198 Annotation: curr.annotation, 199 } 200 if i.idx < len(i.metadatas) { 201 value.Metadata = i.metadatas[i.idx] 202 } 203 return value 204 } 205 206 func (i *testIter) Reset() error { 207 i.idx = -1 208 return nil 209 } 210 211 func (i *testIter) Error() error { 212 return nil 213 } 214 215 func (i *testIter) SetCurrentMetadata(metadata ts.Metadata) { 216 i.metadatas[i.idx] = metadata 217 } 218 219 func TestDownsampleAndWrite(t *testing.T) { 220 ctrl := gomock.NewController(t) 221 defer ctrl.Finish() 222 223 downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl, 224 testDownsamplerAndWriterOptions{}) 225 226 expectDefaultDownsampling(ctrl, testDatapoints1, downsampler, zeroDownsamplerAppenderOpts) 227 expectDefaultStorageWrites(session, testDatapoints1, testAnnotation1) 228 229 err := downAndWrite.Write( 230 context.Background(), testTags1, testDatapoints1, xtime.Second, testAnnotation1, defaultOverride, source) 231 require.NoError(t, err) 232 } 233 234 func TestDownsampleAndWriteWithBadTags(t *testing.T) { 235 ctrl := gomock.NewController(t) 236 defer ctrl.Finish() 237 238 downAndWrite, _, _ := newTestDownsamplerAndWriter(t, ctrl, 239 testDownsamplerAndWriterOptions{}) 240 241 err := downAndWrite.Write( 242 context.Background(), testBadTags, testDatapoints1, xtime.Second, testAnnotation1, defaultOverride, source) 243 require.Error(t, err) 244 245 // Make sure we get a validation error for downsample code path 246 // and for the raw unaggregate write code path. 247 multiErr, ok := xerrors.GetInnerMultiError(err) 248 require.True(t, ok) 249 require.Equal(t, 2, multiErr.NumErrors()) 250 // Make sure all are invalid params errors. 251 for _, err := range multiErr.Errors() { 252 require.True(t, xerrors.IsInvalidParams(err)) 253 } 254 } 255 256 func TestDownsampleAndWriteWithDownsampleOverridesAndNoMappingRules(t *testing.T) { 257 ctrl := gomock.NewController(t) 258 defer ctrl.Finish() 259 260 downAndWrite, _, session := newTestDownsamplerAndWriter(t, ctrl, 261 testDownsamplerAndWriterOptions{}) 262 263 // We're overriding the downsampling with zero mapping rules, so we expect no data to be sent 264 // to the downsampler, but everything to be written to storage. 265 overrides := WriteOptions{ 266 DownsampleOverride: true, 267 DownsampleMappingRules: nil, 268 } 269 270 expectDefaultStorageWrites(session, testDatapoints1, testAnnotation1) 271 272 err := downAndWrite.Write(context.Background(), 273 testTags1, testDatapoints1, xtime.Second, testAnnotation1, overrides, source) 274 require.NoError(t, err) 275 } 276 277 func TestDownsampleAndWriteWithDownsampleOverridesAndMappingRules(t *testing.T) { 278 ctrl := gomock.NewController(t) 279 defer ctrl.Finish() 280 281 downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl, 282 testDownsamplerAndWriterOptions{}) 283 284 // We're overriding the downsampling with mapping rules, so we expect data to be 285 // sent to the downsampler, as well as everything being written to storage. 286 mappingRules := []downsample.AutoMappingRule{ 287 { 288 Aggregations: []aggregation.Type{aggregation.Mean}, 289 Policies: []policy.StoragePolicy{ 290 policy.NewStoragePolicy( 291 time.Minute, xtime.Second, 48*time.Hour), 292 }, 293 }, 294 } 295 overrides := WriteOptions{ 296 DownsampleOverride: true, 297 DownsampleMappingRules: mappingRules, 298 } 299 300 expectedSamplesAppenderOptions := downsample.SampleAppenderOptions{ 301 Override: true, 302 OverrideRules: downsample.SamplesAppenderOverrideRules{ 303 MappingRules: mappingRules, 304 }, 305 } 306 307 expectDefaultDownsampling(ctrl, testDatapoints1, downsampler, expectedSamplesAppenderOptions) 308 expectDefaultStorageWrites(session, testDatapoints1, testAnnotation1) 309 310 err := downAndWrite.Write( 311 context.Background(), testTags1, testDatapoints1, xtime.Second, testAnnotation1, overrides, source) 312 require.NoError(t, err) 313 } 314 315 func TestDownsampleAndWriteWithDownsampleOverridesAndDropMappingRules(t *testing.T) { 316 ctrl := gomock.NewController(t) 317 defer ctrl.Finish() 318 319 downAndWrite, downsampler, _ := newTestDownsamplerAndWriter(t, ctrl, 320 testDownsamplerAndWriterOptions{}) 321 322 // We're overriding the downsampling with mapping rules, so we expect data to be 323 // sent to the downsampler, as well as everything being written to storage. 324 mappingRules := []downsample.AutoMappingRule{ 325 { 326 Aggregations: []aggregation.Type{aggregation.Mean}, 327 Policies: []policy.StoragePolicy{ 328 policy.NewStoragePolicy( 329 time.Minute, xtime.Second, 48*time.Hour), 330 }, 331 }, 332 } 333 overrides := WriteOptions{ 334 DownsampleOverride: true, 335 DownsampleMappingRules: mappingRules, 336 } 337 338 expectedSamplesAppenderOptions := downsample.SampleAppenderOptions{ 339 Override: true, 340 OverrideRules: downsample.SamplesAppenderOverrideRules{ 341 MappingRules: mappingRules, 342 }, 343 } 344 345 var ( 346 mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl) 347 mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl) 348 ) 349 350 mockMetricsAppender. 351 EXPECT(). 352 SamplesAppender(expectedSamplesAppenderOptions). 353 Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender, IsDropPolicyApplied: true}, nil) 354 for _, tag := range testTags1.Tags { 355 mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value) 356 } 357 358 for _, dp := range testDatapoints1 { 359 mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation1) 360 } 361 downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil) 362 363 mockMetricsAppender.EXPECT().Finalize() 364 365 err := downAndWrite.Write( 366 context.Background(), testTags1, testDatapoints1, xtime.Second, testAnnotation1, overrides, source) 367 require.NoError(t, err) 368 } 369 370 func TestDownsampleAndWriteWithWriteOverridesAndNoStoragePolicies(t *testing.T) { 371 ctrl := gomock.NewController(t) 372 defer ctrl.Finish() 373 374 downAndWrite, downsampler, _ := newTestDownsamplerAndWriter(t, ctrl, 375 testDownsamplerAndWriterOptions{}) 376 377 // We're overriding the write with zero storage policies, so we expect no data to be sent 378 // to the storage, but everything to be written to the downsampler with the default settings. 379 overrides := WriteOptions{ 380 WriteOverride: true, 381 WriteStoragePolicies: nil, 382 } 383 384 expectDefaultDownsampling(ctrl, testDatapoints1, downsampler, zeroDownsamplerAppenderOpts) 385 386 err := downAndWrite.Write( 387 context.Background(), testTags1, testDatapoints1, xtime.Second, testAnnotation1, overrides, source) 388 require.NoError(t, err) 389 } 390 391 func TestDownsampleAndWriteWithWriteOverridesAndStoragePolicies(t *testing.T) { 392 ctrl := gomock.NewController(t) 393 defer ctrl.Finish() 394 395 aggregatedNamespaces := []m3.AggregatedClusterNamespaceDefinition{ 396 { 397 NamespaceID: ident.StringID("1m:48h"), 398 Resolution: time.Minute, 399 Retention: 48 * time.Hour, 400 }, 401 { 402 NamespaceID: ident.StringID("10:24h"), 403 Resolution: 10 * time.Second, 404 Retention: 24 * time.Hour, 405 }, 406 } 407 downAndWrite, downsampler, session := newTestDownsamplerAndWriterWithAggregatedNamespace( 408 t, ctrl, aggregatedNamespaces) 409 410 // We're overriding the write with storage policies, so we expect data to be sent to the 411 // storage with the specified policies, but everything to be written to the downsampler 412 // with the default settings. 413 overrides := WriteOptions{ 414 WriteOverride: true, 415 WriteStoragePolicies: []policy.StoragePolicy{ 416 policy.NewStoragePolicy( 417 time.Minute, xtime.Second, 48*time.Hour), 418 policy.NewStoragePolicy( 419 10*time.Second, xtime.Second, 24*time.Hour), 420 }, 421 } 422 423 expectDefaultDownsampling(ctrl, testDatapoints1, downsampler, zeroDownsamplerAppenderOpts) 424 425 // All the datapoints will get written for each of the namespaces. 426 for range aggregatedNamespaces { 427 for _, dp := range testDatapoints1 { 428 session.EXPECT().WriteTagged( 429 gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), gomock.Any()) 430 } 431 } 432 433 err := downAndWrite.Write( 434 context.Background(), testTags1, testDatapoints1, xtime.Second, testAnnotation1, overrides, source) 435 require.NoError(t, err) 436 } 437 438 func TestDownsampleAndWriteNoDownsampler(t *testing.T) { 439 ctrl := gomock.NewController(t) 440 defer ctrl.Finish() 441 442 downAndWrite, _, session := newTestDownsamplerAndWriterWithEnabled(t, ctrl, false, 443 testDownsamplerAndWriterOptions{}) 444 445 expectDefaultStorageWrites(session, testDatapoints1, testAnnotation1) 446 447 err := downAndWrite.Write( 448 context.Background(), testTags1, testDatapoints1, xtime.Second, testAnnotation1, defaultOverride, source) 449 require.NoError(t, err) 450 } 451 452 func TestDownsampleAndWriteBatch(t *testing.T) { 453 ctrl := gomock.NewController(t) 454 defer ctrl.Finish() 455 456 downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl, 457 testDownsamplerAndWriterOptions{}) 458 459 var ( 460 mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl) 461 mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl) 462 ) 463 464 mockMetricsAppender. 465 EXPECT(). 466 SamplesAppender(zeroDownsamplerAppenderOpts). 467 Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil).Times(2) 468 for _, tag := range testTags1.Tags { 469 mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value) 470 } 471 for _, dp := range testDatapoints1 { 472 mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation1) 473 } 474 for _, tag := range testTags2.Tags { 475 mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value) 476 } 477 for _, dp := range testDatapoints2 { 478 mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation2) 479 } 480 downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil) 481 482 mockMetricsAppender.EXPECT().NextMetric().Times(2) 483 mockMetricsAppender.EXPECT().Finalize() 484 485 for _, entry := range testEntries { 486 for _, dp := range entry.datapoints { 487 session.EXPECT().WriteTagged( 488 gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), entry.annotation, 489 ) 490 } 491 } 492 493 iter := newTestIter(testEntries) 494 err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{}) 495 require.NoError(t, err) 496 } 497 498 func TestDownsampleAndWriteBatchBadTags(t *testing.T) { 499 ctrl := gomock.NewController(t) 500 defer ctrl.Finish() 501 502 downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl, 503 testDownsamplerAndWriterOptions{}) 504 505 var ( 506 mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl) 507 mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl) 508 ) 509 510 entries := []testIterEntry{ 511 {tags: testBadTags, datapoints: testDatapoints1, attributes: testAttributesGauge, annotation: testAnnotation1}, 512 {tags: testTags2, datapoints: testDatapoints2, attributes: testAttributesGauge, annotation: testAnnotation2}, 513 } 514 515 // Only expect to write non-bad tags. 516 mockMetricsAppender. 517 EXPECT(). 518 SamplesAppender(zeroDownsamplerAppenderOpts). 519 Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil).Times(1) 520 for _, tag := range testTags2.Tags { 521 mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value) 522 } 523 for _, dp := range testDatapoints2 { 524 mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation2) 525 } 526 downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil) 527 528 mockMetricsAppender.EXPECT().NextMetric().Times(2) 529 mockMetricsAppender.EXPECT().Finalize() 530 531 // Only expect to write non-bad tags. 532 for _, entry := range testEntries[1:] { 533 for _, dp := range entry.datapoints { 534 session.EXPECT().WriteTagged( 535 gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), entry.annotation, 536 ) 537 } 538 } 539 540 iter := newTestIter(entries) 541 err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{}) 542 require.Error(t, err) 543 544 // Make sure we get a validation error for downsample code path 545 // and for the raw unaggregate write code path. 546 multiErr, ok := err.(xerrors.MultiError) 547 require.True(t, ok) 548 require.Equal(t, 2, multiErr.NumErrors()) 549 // Make sure all are invalid params errors. 550 for _, err := range multiErr.Errors() { 551 require.True(t, xerrors.IsInvalidParams(err)) 552 } 553 } 554 555 func TestDownsampleAndWriteBatchDifferentTypes(t *testing.T) { 556 ctrl := gomock.NewController(t) 557 defer ctrl.Finish() 558 559 downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl, 560 testDownsamplerAndWriterOptions{}) 561 562 var ( 563 mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl) 564 mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl) 565 ) 566 567 mockMetricsAppender. 568 EXPECT(). 569 SamplesAppender(downsample.SampleAppenderOptions{ 570 SeriesAttributes: ts.SeriesAttributes{M3Type: ts.M3MetricTypeCounter}, 571 }).Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil). 572 Times(1) 573 mockMetricsAppender. 574 EXPECT(). 575 SamplesAppender(downsample.SampleAppenderOptions{ 576 SeriesAttributes: ts.SeriesAttributes{M3Type: ts.M3MetricTypeTimer}, 577 }).Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil). 578 Times(1) 579 for _, tag := range testTags1.Tags { 580 mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value) 581 } 582 for _, dp := range testDatapoints1 { 583 mockSamplesAppender.EXPECT().AppendCounterSample(dp.Timestamp, int64(dp.Value), testAnnotation1) 584 } 585 for _, tag := range testTags2.Tags { 586 mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value) 587 } 588 for _, dp := range testDatapoints2 { 589 mockSamplesAppender.EXPECT().AppendTimerSample(dp.Timestamp, dp.Value, testAnnotation2) 590 } 591 downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil) 592 593 mockMetricsAppender.EXPECT().NextMetric().Times(2) 594 mockMetricsAppender.EXPECT().Finalize() 595 596 for _, entry := range testEntries2 { 597 for _, dp := range entry.datapoints { 598 session.EXPECT().WriteTagged( 599 gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), entry.annotation, 600 ) 601 } 602 } 603 604 iter := newTestIter(testEntries2) 605 err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{}) 606 require.NoError(t, err) 607 } 608 609 func TestDownsampleAndWriteBatchSingleDrop(t *testing.T) { 610 ctrl := gomock.NewController(t) 611 defer ctrl.Finish() 612 613 downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl, 614 testDownsamplerAndWriterOptions{}) 615 616 var ( 617 mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl) 618 mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl) 619 ) 620 621 mockMetricsAppender. 622 EXPECT(). 623 SamplesAppender(zeroDownsamplerAppenderOpts). 624 Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender, IsDropPolicyApplied: true}, nil).Times(1) 625 mockMetricsAppender. 626 EXPECT(). 627 SamplesAppender(zeroDownsamplerAppenderOpts). 628 Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil).Times(1) 629 for _, tag := range testTags1.Tags { 630 mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value) 631 } 632 for _, dp := range testDatapoints1 { 633 mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation1) 634 } 635 for _, tag := range testTags2.Tags { 636 mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value) 637 } 638 for _, dp := range testDatapoints2 { 639 mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation2) 640 } 641 downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil) 642 643 mockMetricsAppender.EXPECT().NextMetric().Times(2) 644 mockMetricsAppender.EXPECT().Finalize() 645 646 for _, dp := range testEntries[1].datapoints { 647 session.EXPECT().WriteTagged( 648 gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), testEntries[1].annotation, 649 ) 650 } 651 652 iter := newTestIter(testEntries) 653 err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{}) 654 require.NoError(t, err) 655 } 656 657 func TestDownsampleAndWriteBatchDropTimestamp(t *testing.T) { 658 ctrl := gomock.NewController(t) 659 defer ctrl.Finish() 660 661 downAndWrite, downsampler, session := newTestDownsamplerAndWriter(t, ctrl, 662 testDownsamplerAndWriterOptions{}) 663 664 var ( 665 mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl) 666 mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl) 667 ) 668 669 mockMetricsAppender. 670 EXPECT(). 671 SamplesAppender(zeroDownsamplerAppenderOpts). 672 Return(downsample.SamplesAppenderResult{ 673 SamplesAppender: mockSamplesAppender, 674 ShouldDropTimestamp: true, 675 }, nil).Times(1) 676 mockMetricsAppender. 677 EXPECT(). 678 SamplesAppender(zeroDownsamplerAppenderOpts). 679 Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil).Times(1) 680 for _, tag := range testTags1.Tags { 681 mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value) 682 } 683 for _, dp := range testDatapoints1 { 684 mockSamplesAppender.EXPECT().AppendUntimedGaugeSample(dp.Timestamp, dp.Value, testAnnotation1) 685 } 686 for _, tag := range testTags2.Tags { 687 mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value) 688 } 689 for _, dp := range testDatapoints2 { 690 mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation2) 691 } 692 downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil) 693 694 mockMetricsAppender.EXPECT().NextMetric().Times(2) 695 mockMetricsAppender.EXPECT().Finalize() 696 697 for _, dp := range testEntries[0].datapoints { 698 session.EXPECT().WriteTagged( 699 gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), testEntries[0].annotation, 700 ) 701 } 702 703 for _, dp := range testEntries[1].datapoints { 704 session.EXPECT().WriteTagged( 705 gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), testEntries[1].annotation, 706 ) 707 } 708 709 iter := newTestIter(testEntries) 710 err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{}) 711 require.NoError(t, err) 712 } 713 714 func TestDownsampleAndWriteBatchNoDownsampler(t *testing.T) { 715 ctrl := gomock.NewController(t) 716 defer ctrl.Finish() 717 718 downAndWrite, _, session := newTestDownsamplerAndWriterWithEnabled(t, ctrl, false, 719 testDownsamplerAndWriterOptions{}) 720 721 for _, entry := range testEntries { 722 for _, dp := range entry.datapoints { 723 session.EXPECT().WriteTagged( 724 gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), entry.annotation, 725 ) 726 } 727 } 728 729 iter := newTestIter(testEntries) 730 err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{}) 731 require.NoError(t, err) 732 } 733 734 func TestDownsampleAndWriteBatchOverrideDownsampleRules(t *testing.T) { 735 ctrl := gomock.NewController(t) 736 defer ctrl.Finish() 737 738 downAndWrite, downsampler, _ := newTestDownsamplerAndWriter(t, ctrl, 739 testDownsamplerAndWriterOptions{}) 740 741 var ( 742 mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl) 743 mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl) 744 overrideMappingRules = []downsample.AutoMappingRule{ 745 { 746 Aggregations: []aggregation.Type{ 747 aggregation.Sum, 748 }, 749 Policies: policy.StoragePolicies{ 750 policy.MustParseStoragePolicy("1h:30d"), 751 }, 752 }, 753 } 754 ) 755 756 mockMetricsAppender. 757 EXPECT(). 758 SamplesAppender(downsample.SampleAppenderOptions{ 759 Override: true, 760 OverrideRules: downsample.SamplesAppenderOverrideRules{ 761 MappingRules: overrideMappingRules, 762 }, 763 }). 764 Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil) 765 766 entries := testEntries[:1] 767 for _, entry := range entries { 768 for _, tag := range entry.tags.Tags { 769 mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value) 770 } 771 // We will also get the common gauge tag. 772 for _, dp := range entry.datapoints { 773 mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation1) 774 } 775 } 776 downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil) 777 778 mockMetricsAppender.EXPECT().NextMetric() 779 mockMetricsAppender.EXPECT().Finalize() 780 781 iter := newTestIter(entries) 782 err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{ 783 DownsampleOverride: true, 784 DownsampleMappingRules: overrideMappingRules, 785 WriteOverride: true, 786 WriteStoragePolicies: nil, 787 }) 788 require.NoError(t, err) 789 } 790 791 func TestDownsampleAndWriteBatchOverrideStoragePolicies(t *testing.T) { 792 ctrl := gomock.NewController(t) 793 defer ctrl.Finish() 794 795 testOpts := testDownsamplerAndWriterOptions{ 796 aggregatedNamespaces: []m3.AggregatedClusterNamespaceDefinition{ 797 { 798 NamespaceID: ident.StringID("namespace_10m_7d"), 799 Resolution: 10 * time.Minute, 800 Retention: 7 * 24 * time.Hour, 801 }, 802 { 803 NamespaceID: ident.StringID("namespace_1h_60d"), 804 Resolution: time.Hour, 805 Retention: 60 * 24 * time.Hour, 806 }, 807 }, 808 } 809 downAndWrite, _, session := newTestDownsamplerAndWriter(t, ctrl, testOpts) 810 811 entries := testEntries[:1] 812 for _, namespace := range testOpts.aggregatedNamespaces { 813 for _, entry := range entries { 814 for _, dp := range entry.datapoints { 815 namespaceMatcher := ident.NewIDMatcher(namespace.NamespaceID.String()) 816 session.EXPECT().WriteTagged( 817 namespaceMatcher, gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), entry.annotation, 818 ) 819 } 820 } 821 } 822 823 iter := newTestIter(entries) 824 err := downAndWrite.WriteBatch(context.Background(), iter, WriteOptions{ 825 DownsampleOverride: true, 826 DownsampleMappingRules: nil, 827 WriteOverride: true, 828 WriteStoragePolicies: policy.StoragePolicies{ 829 policy.MustParseStoragePolicy("10m:7d"), 830 policy.MustParseStoragePolicy("1h:60d"), 831 }, 832 }) 833 require.NoError(t, err) 834 } 835 836 func expectDefaultDownsampling( 837 ctrl *gomock.Controller, datapoints []ts.Datapoint, 838 downsampler *downsample.MockDownsampler, downsampleOpts downsample.SampleAppenderOptions) { 839 var ( 840 mockSamplesAppender = downsample.NewMockSamplesAppender(ctrl) 841 mockMetricsAppender = downsample.NewMockMetricsAppender(ctrl) 842 ) 843 844 mockMetricsAppender. 845 EXPECT(). 846 SamplesAppender(downsampleOpts). 847 Return(downsample.SamplesAppenderResult{SamplesAppender: mockSamplesAppender}, nil) 848 for _, tag := range testTags1.Tags { 849 mockMetricsAppender.EXPECT().AddTag(tag.Name, tag.Value) 850 } 851 852 for _, dp := range datapoints { 853 mockSamplesAppender.EXPECT().AppendGaugeSample(dp.Timestamp, dp.Value, testAnnotation1) 854 } 855 downsampler.EXPECT().NewMetricsAppender().Return(mockMetricsAppender, nil) 856 857 mockMetricsAppender.EXPECT().Finalize() 858 } 859 860 func expectDefaultStorageWrites(session *client.MockSession, datapoints []ts.Datapoint, annotation []byte) { 861 for _, dp := range datapoints { 862 session.EXPECT().WriteTagged( 863 gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), dp.Value, gomock.Any(), annotation) 864 } 865 } 866 867 type testDownsamplerAndWriterOptions struct { 868 aggregatedNamespaces []m3.AggregatedClusterNamespaceDefinition 869 } 870 871 func newTestDownsamplerAndWriter( 872 t *testing.T, 873 ctrl *gomock.Controller, 874 opts testDownsamplerAndWriterOptions, 875 ) (*downsamplerAndWriter, *downsample.MockDownsampler, *client.MockSession) { 876 return newTestDownsamplerAndWriterWithEnabled(t, ctrl, true, opts) 877 } 878 879 func newTestDownsamplerAndWriterWithEnabled( 880 t *testing.T, 881 ctrl *gomock.Controller, 882 enabled bool, 883 opts testDownsamplerAndWriterOptions, 884 ) (*downsamplerAndWriter, *downsample.MockDownsampler, *client.MockSession) { 885 var ( 886 storage storage.Storage 887 session *client.MockSession 888 ) 889 if ns := opts.aggregatedNamespaces; len(ns) > 0 { 890 storage, session = testm3.NewStorageAndSessionWithAggregatedNamespaces(t, ctrl, ns) 891 } else { 892 storage, session = testm3.NewStorageAndSession(t, ctrl) 893 } 894 downsampler := downsample.NewMockDownsampler(ctrl) 895 downsampler.EXPECT().Enabled().Return(enabled) 896 return NewDownsamplerAndWriter(storage, downsampler, testWorkerPool, 897 instrument.NewOptions()).(*downsamplerAndWriter), downsampler, session 898 } 899 900 func newTestDownsamplerAndWriterWithAggregatedNamespace( 901 t *testing.T, 902 ctrl *gomock.Controller, 903 aggregatedNamespaces []m3.AggregatedClusterNamespaceDefinition, 904 ) (*downsamplerAndWriter, *downsample.MockDownsampler, *client.MockSession) { 905 storage, session := testm3.NewStorageAndSessionWithAggregatedNamespaces( 906 t, ctrl, aggregatedNamespaces) 907 downsampler := downsample.NewMockDownsampler(ctrl) 908 downsampler.EXPECT().Enabled().Return(true) 909 return NewDownsamplerAndWriter(storage, downsampler, testWorkerPool, 910 instrument.NewOptions()).(*downsamplerAndWriter), downsampler, session 911 } 912 913 func init() { 914 var err error 915 testWorkerPool, err = xsync.NewPooledWorkerPool( 916 16, 917 xsync.NewPooledWorkerPoolOptions(). 918 SetGrowOnDemand(true), 919 ) 920 921 if err != nil { 922 panic(fmt.Sprintf("unable to create pooled worker pool: %v", err)) 923 } 924 925 testWorkerPool.Init() 926 }