github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/aggregator/client/tcp_client_test.go (about) 1 // Copyright (c) 2018 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 //nolint:dupl,exhaustive 22 package client 23 24 import ( 25 "errors" 26 "math" 27 "strings" 28 "testing" 29 "time" 30 31 "github.com/golang/mock/gomock" 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 35 "github.com/m3db/m3/src/cluster/generated/proto/placementpb" 36 "github.com/m3db/m3/src/cluster/kv/mem" 37 "github.com/m3db/m3/src/cluster/placement" 38 "github.com/m3db/m3/src/cluster/shard" 39 "github.com/m3db/m3/src/metrics/aggregation" 40 "github.com/m3db/m3/src/metrics/metadata" 41 "github.com/m3db/m3/src/metrics/metric" 42 "github.com/m3db/m3/src/metrics/metric/aggregated" 43 "github.com/m3db/m3/src/metrics/metric/unaggregated" 44 "github.com/m3db/m3/src/metrics/pipeline" 45 "github.com/m3db/m3/src/metrics/pipeline/applied" 46 "github.com/m3db/m3/src/metrics/policy" 47 "github.com/m3db/m3/src/x/clock" 48 "github.com/m3db/m3/src/x/instrument" 49 xtime "github.com/m3db/m3/src/x/time" 50 ) 51 52 var ( 53 testNowNanos = time.Now().UnixNano() 54 testCutoverNanos = testNowNanos - int64(time.Minute) 55 testCutoffNanos = testNowNanos + int64(time.Hour) 56 testCounter = unaggregated.MetricUnion{ 57 Type: metric.CounterType, 58 ID: []byte("foo"), 59 CounterVal: 1234, 60 } 61 testBatchTimer = unaggregated.MetricUnion{ 62 Type: metric.TimerType, 63 ID: []byte("foo"), 64 BatchTimerVal: []float64{222.22, 345.67, 901.23345}, 65 } 66 testGauge = unaggregated.MetricUnion{ 67 Type: metric.GaugeType, 68 ID: []byte("foo"), 69 GaugeVal: 123.456, 70 } 71 testTimed = aggregated.Metric{ 72 Type: metric.CounterType, 73 ID: []byte("testTimed"), 74 TimeNanos: 1234, 75 Value: 178, 76 } 77 testForwarded = aggregated.ForwardedMetric{ 78 Type: metric.CounterType, 79 ID: []byte("testForwarded"), 80 TimeNanos: 1234, 81 Values: []float64{34567, 256, 178}, 82 } 83 testPassthrough = aggregated.Metric{ 84 Type: metric.CounterType, 85 ID: []byte("testPassthrough"), 86 TimeNanos: 12345, 87 Value: 123, 88 } 89 testStagedMetadatas = metadata.StagedMetadatas{ 90 { 91 CutoverNanos: 100, 92 Tombstoned: false, 93 Metadata: metadata.Metadata{ 94 Pipelines: []metadata.PipelineMetadata{ 95 { 96 AggregationID: aggregation.DefaultID, 97 StoragePolicies: []policy.StoragePolicy{ 98 policy.NewStoragePolicy(20*time.Second, xtime.Second, 6*time.Hour), 99 policy.NewStoragePolicy(time.Minute, xtime.Minute, 2*24*time.Hour), 100 policy.NewStoragePolicy(10*time.Minute, xtime.Minute, 25*24*time.Hour), 101 }, 102 }, 103 }, 104 }, 105 }, 106 { 107 CutoverNanos: 200, 108 Tombstoned: true, 109 Metadata: metadata.Metadata{ 110 Pipelines: []metadata.PipelineMetadata{ 111 { 112 AggregationID: aggregation.DefaultID, 113 StoragePolicies: []policy.StoragePolicy{ 114 policy.NewStoragePolicy(time.Second, xtime.Second, time.Hour), 115 }, 116 }, 117 }, 118 }, 119 }, 120 } 121 testTimedMetadata = metadata.TimedMetadata{ 122 AggregationID: aggregation.DefaultID, 123 StoragePolicy: policy.NewStoragePolicy(time.Minute, xtime.Minute, 12*time.Hour), 124 } 125 testForwardMetadata = metadata.ForwardMetadata{ 126 AggregationID: aggregation.DefaultID, 127 StoragePolicy: policy.NewStoragePolicy(time.Minute, xtime.Minute, 12*time.Hour), 128 Pipeline: applied.NewPipeline([]applied.OpUnion{ 129 { 130 Type: pipeline.RollupOpType, 131 Rollup: applied.RollupOp{ 132 ID: []byte("foo"), 133 AggregationID: aggregation.MustCompressTypes(aggregation.Count), 134 }, 135 }, 136 }), 137 SourceID: 1234, 138 NumForwardedTimes: 3, 139 } 140 testPassthroughMetadata = policy.NewStoragePolicy(time.Minute, xtime.Minute, 12*time.Hour) 141 testPlacementInstances = []placement.Instance{ 142 placement.NewInstance(). 143 SetID("instance1"). 144 SetEndpoint("instance1_endpoint"). 145 SetShards(shard.NewShards([]shard.Shard{ 146 shard.NewShard(0). 147 SetState(shard.Initializing). 148 SetCutoverNanos(testCutoverNanos). 149 SetCutoffNanos(testCutoffNanos), 150 shard.NewShard(1). 151 SetState(shard.Initializing). 152 SetCutoverNanos(testCutoverNanos). 153 SetCutoffNanos(testCutoffNanos), 154 })), 155 placement.NewInstance(). 156 SetID("instance2"). 157 SetEndpoint("instance2_endpoint"). 158 SetShards(shard.NewShards([]shard.Shard{ 159 shard.NewShard(2). 160 SetState(shard.Initializing). 161 SetCutoverNanos(testCutoverNanos). 162 SetCutoffNanos(testCutoffNanos), 163 shard.NewShard(3). 164 SetState(shard.Initializing). 165 SetCutoverNanos(testCutoverNanos). 166 SetCutoffNanos(testCutoffNanos), 167 })), 168 placement.NewInstance(). 169 SetID("instance3"). 170 SetEndpoint("instance3_endpoint"). 171 SetShards(shard.NewShards([]shard.Shard{ 172 shard.NewShard(0). 173 SetState(shard.Initializing). 174 SetCutoverNanos(testCutoverNanos). 175 SetCutoffNanos(testCutoffNanos), 176 shard.NewShard(1). 177 SetState(shard.Initializing). 178 SetCutoverNanos(testCutoverNanos). 179 SetCutoffNanos(testCutoffNanos), 180 })), 181 placement.NewInstance(). 182 SetID("instance4"). 183 SetEndpoint("instance4_endpoint"). 184 SetShards(shard.NewShards([]shard.Shard{ 185 shard.NewShard(2). 186 SetState(shard.Initializing). 187 SetCutoverNanos(testCutoverNanos). 188 SetCutoffNanos(testCutoffNanos), 189 shard.NewShard(3). 190 SetState(shard.Initializing). 191 SetCutoverNanos(testCutoverNanos). 192 SetCutoffNanos(testCutoffNanos), 193 })), 194 } 195 testPlacement = placement.NewPlacement(). 196 SetVersion(1). 197 SetCutoverNanos(12345). 198 SetShards([]uint32{0, 1, 2, 3}). 199 SetInstances(testPlacementInstances) 200 ) 201 202 func TestTCPClientWriteUntimedMetricClosed(t *testing.T) { 203 c := mustNewTestTCPClient(t, testOptions()) 204 require.NoError(t, c.Close()) 205 for _, input := range []unaggregated.MetricUnion{testCounter, testBatchTimer, testGauge} { 206 var err error 207 switch input.Type { 208 case metric.CounterType: 209 err = c.WriteUntimedCounter(input.Counter(), testStagedMetadatas) 210 case metric.TimerType: 211 err = c.WriteUntimedBatchTimer(input.BatchTimer(), testStagedMetadatas) 212 case metric.GaugeType: 213 err = c.WriteUntimedGauge(input.Gauge(), testStagedMetadatas) 214 } 215 require.Error(t, err) 216 } 217 } 218 219 func TestTCPClientWriteUntimedMetricPlacementError(t *testing.T) { 220 ctrl := gomock.NewController(t) 221 defer ctrl.Finish() 222 223 errInvalidPlacement := errors.New("invalid placement") 224 watcher := placement.NewMockWatcher(ctrl) 225 watcher.EXPECT().Get(). 226 Return(nil, errInvalidPlacement). 227 MinTimes(1) 228 c := mustNewTestTCPClient(t, testOptions()) 229 c.placementWatcher = watcher 230 231 for _, input := range []unaggregated.MetricUnion{testCounter, testBatchTimer, testGauge} { 232 var err error 233 switch input.Type { 234 case metric.CounterType: 235 err = c.WriteUntimedCounter(input.Counter(), testStagedMetadatas) 236 case metric.TimerType: 237 err = c.WriteUntimedBatchTimer(input.BatchTimer(), testStagedMetadatas) 238 case metric.GaugeType: 239 err = c.WriteUntimedGauge(input.Gauge(), testStagedMetadatas) 240 } 241 require.Equal(t, errInvalidPlacement, err) 242 } 243 } 244 245 func TestTCPClientWriteUntimedMetricPlacementNil(t *testing.T) { 246 ctrl := gomock.NewController(t) 247 defer ctrl.Finish() 248 249 watcher := placement.NewMockWatcher(ctrl) 250 watcher.EXPECT().Get(). 251 Return(nil, nil). 252 MinTimes(1) 253 c := mustNewTestTCPClient(t, testOptions()) 254 c.placementWatcher = watcher 255 256 for _, input := range []unaggregated.MetricUnion{testCounter, testBatchTimer, testGauge} { 257 var err error 258 switch input.Type { 259 case metric.CounterType: 260 err = c.WriteUntimedCounter(input.Counter(), testStagedMetadatas) 261 case metric.TimerType: 262 err = c.WriteUntimedBatchTimer(input.BatchTimer(), testStagedMetadatas) 263 case metric.GaugeType: 264 err = c.WriteUntimedGauge(input.Gauge(), testStagedMetadatas) 265 } 266 require.Equal(t, errNilPlacement, err) 267 } 268 } 269 270 func TestTCPClientWriteUntimedMetricSuccess(t *testing.T) { 271 ctrl := gomock.NewController(t) 272 defer ctrl.Finish() 273 274 var ( 275 instancesRes []placement.Instance 276 shardRes uint32 277 payloadRes payloadUnion 278 ) 279 writerMgr := NewMockinstanceWriterManager(ctrl) 280 writerMgr.EXPECT(). 281 Write(gomock.Any(), gomock.Any(), gomock.Any()). 282 DoAndReturn(func( 283 instance placement.Instance, 284 shardID uint32, 285 payload payloadUnion, 286 ) error { 287 instancesRes = append(instancesRes, instance) 288 shardRes = shardID 289 payloadRes = payload 290 return nil 291 }). 292 MinTimes(1) 293 watcher := placement.NewMockWatcher(ctrl) 294 watcher.EXPECT().Get().Return(testPlacement, nil).MinTimes(1) 295 c := mustNewTestTCPClient(t, testOptions()) 296 c.nowFn = func() time.Time { return time.Unix(0, testNowNanos) } 297 c.writerMgr = writerMgr 298 c.placementWatcher = watcher 299 300 expectedInstances := []placement.Instance{ 301 testPlacementInstances[0], 302 testPlacementInstances[2], 303 } 304 for _, input := range []unaggregated.MetricUnion{testCounter, testBatchTimer, testGauge} { 305 // Reset states in each iteration. 306 instancesRes = instancesRes[:0] 307 shardRes = 0 308 payloadRes = payloadUnion{} 309 310 var err error 311 switch input.Type { 312 case metric.CounterType: 313 err = c.WriteUntimedCounter(input.Counter(), testStagedMetadatas) 314 case metric.TimerType: 315 err = c.WriteUntimedBatchTimer(input.BatchTimer(), testStagedMetadatas) 316 case metric.GaugeType: 317 err = c.WriteUntimedGauge(input.Gauge(), testStagedMetadatas) 318 } 319 320 require.NoError(t, err) 321 require.Equal(t, expectedInstances, instancesRes) 322 require.Equal(t, uint32(1), shardRes) 323 require.Equal(t, untimedType, payloadRes.payloadType) 324 require.Equal(t, input, payloadRes.untimed.metric) 325 require.Equal(t, testStagedMetadatas, payloadRes.untimed.metadatas) 326 } 327 } 328 329 func TestTCPClientWriteUntimedMetricPartialError(t *testing.T) { 330 ctrl := gomock.NewController(t) 331 defer ctrl.Finish() 332 333 var ( 334 instancesRes []placement.Instance 335 shardRes uint32 336 payloadRes payloadUnion 337 errInstanceWrite = errors.New("instance write error") 338 ) 339 writerMgr := NewMockinstanceWriterManager(ctrl) 340 writerMgr.EXPECT(). 341 Write(gomock.Any(), gomock.Any(), gomock.Any()). 342 DoAndReturn(func( 343 instance placement.Instance, 344 shardID uint32, 345 payload payloadUnion, 346 ) error { 347 if instance.ID() == testPlacementInstances[0].ID() { 348 return errInstanceWrite 349 } 350 instancesRes = append(instancesRes, instance) 351 shardRes = shardID 352 payloadRes = payload 353 return nil 354 }). 355 MinTimes(1) 356 watcher := placement.NewMockWatcher(ctrl) 357 watcher.EXPECT().Get().Return(testPlacement, nil).MinTimes(1) 358 c := mustNewTestTCPClient(t, testOptions()) 359 c.nowFn = func() time.Time { return time.Unix(0, testNowNanos) } 360 c.writerMgr = writerMgr 361 c.placementWatcher = watcher 362 363 expectedInstances := []placement.Instance{ 364 testPlacementInstances[2], 365 } 366 err := c.WriteUntimedCounter(testCounter.Counter(), testStagedMetadatas) 367 require.Error(t, err) 368 require.True(t, strings.Contains(err.Error(), errInstanceWrite.Error())) 369 require.Equal(t, expectedInstances, instancesRes) 370 require.Equal(t, uint32(1), shardRes) 371 require.Equal(t, untimedType, payloadRes.payloadType) 372 require.Equal(t, testCounter, payloadRes.untimed.metric) 373 require.Equal(t, testStagedMetadatas, payloadRes.untimed.metadatas) 374 } 375 376 func TestTCPClientWriteUntimedMetricBeforeShardCutover(t *testing.T) { 377 ctrl := gomock.NewController(t) 378 defer ctrl.Finish() 379 380 var instancesRes []placement.Instance 381 watcher := placement.NewMockWatcher(ctrl) 382 watcher.EXPECT().Get().Return(testPlacement, nil).MinTimes(1) 383 c := mustNewTestTCPClient(t, testOptions()) 384 c.shardCutoverWarmupDuration = time.Second 385 c.nowFn = func() time.Time { return time.Unix(0, testCutoverNanos-1).Add(-time.Second) } 386 c.writerMgr = nil 387 c.placementWatcher = watcher 388 389 err := c.WriteUntimedCounter(testCounter.Counter(), testStagedMetadatas) 390 require.NoError(t, err) 391 require.Nil(t, instancesRes) 392 } 393 394 func TestTCPClientWriteUntimedMetricAfterShardCutoff(t *testing.T) { 395 ctrl := gomock.NewController(t) 396 defer ctrl.Finish() 397 398 var instancesRes []placement.Instance 399 watcher := placement.NewMockWatcher(ctrl) 400 watcher.EXPECT().Get().Return(testPlacement, nil).MinTimes(1) 401 c := mustNewTestTCPClient(t, testOptions()) 402 c.shardCutoffLingerDuration = time.Second 403 c.nowFn = func() time.Time { return time.Unix(0, testCutoffNanos+1).Add(time.Second) } 404 c.writerMgr = nil 405 c.placementWatcher = watcher 406 407 err := c.WriteUntimedCounter(testCounter.Counter(), testStagedMetadatas) 408 require.NoError(t, err) 409 require.Nil(t, instancesRes) 410 } 411 412 func TestTCPClientWriteTimedMetricSuccess(t *testing.T) { 413 ctrl := gomock.NewController(t) 414 defer ctrl.Finish() 415 416 var ( 417 instancesRes []placement.Instance 418 shardRes uint32 419 payloadRes payloadUnion 420 ) 421 writerMgr := NewMockinstanceWriterManager(ctrl) 422 writerMgr.EXPECT(). 423 Write(gomock.Any(), gomock.Any(), gomock.Any()). 424 DoAndReturn(func( 425 instance placement.Instance, 426 shardID uint32, 427 payload payloadUnion, 428 ) error { 429 instancesRes = append(instancesRes, instance) 430 shardRes = shardID 431 payloadRes = payload 432 return nil 433 }). 434 MinTimes(1) 435 watcher := placement.NewMockWatcher(ctrl) 436 watcher.EXPECT().Get().Return(testPlacement, nil).MinTimes(1) 437 c := mustNewTestTCPClient(t, testOptions()) 438 c.nowFn = func() time.Time { return time.Unix(0, testNowNanos) } 439 c.writerMgr = writerMgr 440 c.placementWatcher = watcher 441 442 expectedInstances := []placement.Instance{ 443 testPlacementInstances[0], 444 testPlacementInstances[2], 445 } 446 testMetric := testTimed 447 testMetric.TimeNanos = testNowNanos 448 err := c.WriteTimed(testMetric, testTimedMetadata) 449 require.NoError(t, err) 450 require.Equal(t, expectedInstances, instancesRes) 451 require.Equal(t, uint32(1), shardRes) 452 require.Equal(t, timedType, payloadRes.payloadType) 453 require.Equal(t, testMetric, payloadRes.timed.metric) 454 require.Equal(t, testTimedMetadata, payloadRes.timed.metadata) 455 } 456 457 func TestTCPClientWriteTimedMetricPartialError(t *testing.T) { 458 ctrl := gomock.NewController(t) 459 defer ctrl.Finish() 460 461 var ( 462 instancesRes []placement.Instance 463 shardRes uint32 464 payloadRes payloadUnion 465 errInstanceWrite = errors.New("instance write error") 466 ) 467 writerMgr := NewMockinstanceWriterManager(ctrl) 468 writerMgr.EXPECT(). 469 Write(gomock.Any(), gomock.Any(), gomock.Any()). 470 DoAndReturn(func( 471 instance placement.Instance, 472 shardID uint32, 473 payload payloadUnion, 474 ) error { 475 if instance.ID() == testPlacementInstances[0].ID() { 476 return errInstanceWrite 477 } 478 instancesRes = append(instancesRes, instance) 479 shardRes = shardID 480 payloadRes = payload 481 return nil 482 }). 483 MinTimes(1) 484 watcher := placement.NewMockWatcher(ctrl) 485 watcher.EXPECT().Get().Return(testPlacement, nil).MinTimes(1) 486 c := mustNewTestTCPClient(t, testOptions()) 487 c.nowFn = func() time.Time { return time.Unix(0, testNowNanos) } 488 c.writerMgr = writerMgr 489 c.placementWatcher = watcher 490 491 expectedInstances := []placement.Instance{ 492 testPlacementInstances[2], 493 } 494 testMetric := testTimed 495 testMetric.TimeNanos = testNowNanos 496 err := c.WriteTimed(testMetric, testTimedMetadata) 497 require.Error(t, err) 498 require.True(t, strings.Contains(err.Error(), errInstanceWrite.Error())) 499 require.Equal(t, expectedInstances, instancesRes) 500 require.Equal(t, uint32(1), shardRes) 501 require.Equal(t, timedType, payloadRes.payloadType) 502 require.Equal(t, testMetric, payloadRes.timed.metric) 503 require.Equal(t, testTimedMetadata, payloadRes.timed.metadata) 504 } 505 506 func TestTCPClientWriteForwardedMetricSuccess(t *testing.T) { 507 ctrl := gomock.NewController(t) 508 defer ctrl.Finish() 509 510 var ( 511 instancesRes []placement.Instance 512 shardRes uint32 513 payloadRes payloadUnion 514 ) 515 writerMgr := NewMockinstanceWriterManager(ctrl) 516 writerMgr.EXPECT(). 517 Write(gomock.Any(), gomock.Any(), gomock.Any()). 518 DoAndReturn(func( 519 instance placement.Instance, 520 shardID uint32, 521 payload payloadUnion, 522 ) error { 523 instancesRes = append(instancesRes, instance) 524 shardRes = shardID 525 payloadRes = payload 526 return nil 527 }). 528 MinTimes(1) 529 watcher := placement.NewMockWatcher(ctrl) 530 watcher.EXPECT().Get().Return(testPlacement, nil).MinTimes(1) 531 c := mustNewTestTCPClient(t, testOptions()) 532 c.nowFn = func() time.Time { return time.Unix(0, testNowNanos) } 533 c.writerMgr = writerMgr 534 c.placementWatcher = watcher 535 536 expectedInstances := []placement.Instance{ 537 testPlacementInstances[0], 538 testPlacementInstances[2], 539 } 540 testMetric := testForwarded 541 testMetric.TimeNanos = testNowNanos 542 err := c.WriteForwarded(testMetric, testForwardMetadata) 543 require.NoError(t, err) 544 require.Equal(t, expectedInstances, instancesRes) 545 require.Equal(t, uint32(1), shardRes) 546 require.Equal(t, forwardedType, payloadRes.payloadType) 547 require.Equal(t, testMetric, payloadRes.forwarded.metric) 548 require.Equal(t, testForwardMetadata, payloadRes.forwarded.metadata) 549 } 550 551 func TestTCPClientWriteForwardedMetricPartialError(t *testing.T) { 552 ctrl := gomock.NewController(t) 553 defer ctrl.Finish() 554 555 var ( 556 instancesRes []placement.Instance 557 shardRes uint32 558 payloadRes payloadUnion 559 errInstanceWrite = errors.New("instance write error") 560 ) 561 writerMgr := NewMockinstanceWriterManager(ctrl) 562 writerMgr.EXPECT(). 563 Write(gomock.Any(), gomock.Any(), gomock.Any()). 564 DoAndReturn(func( 565 instance placement.Instance, 566 shardID uint32, 567 payload payloadUnion, 568 ) error { 569 if instance.ID() == testPlacementInstances[0].ID() { 570 return errInstanceWrite 571 } 572 instancesRes = append(instancesRes, instance) 573 shardRes = shardID 574 payloadRes = payload 575 return nil 576 }). 577 MinTimes(1) 578 watcher := placement.NewMockWatcher(ctrl) 579 watcher.EXPECT().Get().Return(testPlacement, nil).MinTimes(1) 580 c := mustNewTestTCPClient(t, testOptions()) 581 c.nowFn = func() time.Time { return time.Unix(0, testNowNanos) } 582 c.writerMgr = writerMgr 583 c.placementWatcher = watcher 584 585 expectedInstances := []placement.Instance{ 586 testPlacementInstances[2], 587 } 588 testMetric := testForwarded 589 testMetric.TimeNanos = testNowNanos 590 err := c.WriteForwarded(testMetric, testForwardMetadata) 591 require.Error(t, err) 592 require.True(t, strings.Contains(err.Error(), errInstanceWrite.Error())) 593 require.Equal(t, expectedInstances, instancesRes) 594 require.Equal(t, uint32(1), shardRes) 595 require.Equal(t, forwardedType, payloadRes.payloadType) 596 require.Equal(t, testMetric, payloadRes.forwarded.metric) 597 require.Equal(t, testForwardMetadata, payloadRes.forwarded.metadata) 598 } 599 600 func TestTCPClientWritePassthroughMetricSuccess(t *testing.T) { 601 ctrl := gomock.NewController(t) 602 defer ctrl.Finish() 603 604 var ( 605 instancesRes []placement.Instance 606 shardRes uint32 607 payloadRes payloadUnion 608 ) 609 writerMgr := NewMockinstanceWriterManager(ctrl) 610 writerMgr.EXPECT(). 611 Write(gomock.Any(), gomock.Any(), gomock.Any()). 612 DoAndReturn(func( 613 instance placement.Instance, 614 shardID uint32, 615 payload payloadUnion, 616 ) error { 617 instancesRes = append(instancesRes, instance) 618 shardRes = shardID 619 payloadRes = payload 620 return nil 621 }). 622 MinTimes(1) 623 watcher := placement.NewMockWatcher(ctrl) 624 watcher.EXPECT().Get().Return(testPlacement, nil).MinTimes(1) 625 c := mustNewTestTCPClient(t, testOptions()) 626 c.nowFn = func() time.Time { return time.Unix(0, testNowNanos) } 627 c.writerMgr = writerMgr 628 c.placementWatcher = watcher 629 630 expectedInstances := []placement.Instance{ 631 testPlacementInstances[0], 632 testPlacementInstances[2], 633 } 634 testMetric := testPassthrough 635 testMetric.TimeNanos = testNowNanos 636 err := c.WritePassthrough(testMetric, testPassthroughMetadata) 637 require.NoError(t, err) 638 require.Equal(t, expectedInstances, instancesRes) 639 require.Equal(t, uint32(1), shardRes) 640 require.Equal(t, passthroughType, payloadRes.payloadType) 641 require.Equal(t, testMetric, payloadRes.passthrough.metric) 642 require.Equal(t, testPassthroughMetadata, payloadRes.passthrough.storagePolicy) 643 } 644 645 func TestTCPClientWritePassthroughMetricPartialError(t *testing.T) { 646 ctrl := gomock.NewController(t) 647 defer ctrl.Finish() 648 649 var ( 650 instancesRes []placement.Instance 651 shardRes uint32 652 payloadRes payloadUnion 653 errInstanceWrite = errors.New("instance write error") 654 ) 655 writerMgr := NewMockinstanceWriterManager(ctrl) 656 writerMgr.EXPECT(). 657 Write(gomock.Any(), gomock.Any(), gomock.Any()). 658 DoAndReturn(func( 659 instance placement.Instance, 660 shardID uint32, 661 payload payloadUnion, 662 ) error { 663 if instance.ID() == testPlacementInstances[0].ID() { 664 return errInstanceWrite 665 } 666 instancesRes = append(instancesRes, instance) 667 shardRes = shardID 668 payloadRes = payload 669 return nil 670 }). 671 MinTimes(1) 672 watcher := placement.NewMockWatcher(ctrl) 673 watcher.EXPECT().Get().Return(testPlacement, nil).MinTimes(1) 674 c := mustNewTestTCPClient(t, testOptions()) 675 c.nowFn = func() time.Time { return time.Unix(0, testNowNanos) } 676 c.writerMgr = writerMgr 677 c.placementWatcher = watcher 678 679 expectedInstances := []placement.Instance{ 680 testPlacementInstances[2], 681 } 682 testMetric := testPassthrough 683 testMetric.TimeNanos = testNowNanos 684 err := c.WritePassthrough(testMetric, testPassthroughMetadata) 685 require.Error(t, err) 686 require.True(t, strings.Contains(err.Error(), errInstanceWrite.Error())) 687 require.Equal(t, expectedInstances, instancesRes) 688 require.Equal(t, uint32(1), shardRes) 689 require.Equal(t, passthroughType, payloadRes.payloadType) 690 require.Equal(t, testMetric, payloadRes.passthrough.metric) 691 require.Equal(t, testPassthroughMetadata, payloadRes.passthrough.storagePolicy) 692 } 693 694 func TestTCPClientFlushClosed(t *testing.T) { 695 c := mustNewTestTCPClient(t, testOptions()) 696 require.NoError(t, c.Close()) 697 require.Equal(t, errInstanceWriterManagerClosed, c.Flush()) 698 } 699 700 func TestTCPClientFlushError(t *testing.T) { 701 ctrl := gomock.NewController(t) 702 defer ctrl.Finish() 703 704 errTestFlush := errors.New("test flush error") 705 writerMgr := NewMockinstanceWriterManager(ctrl) 706 writerMgr.EXPECT().Flush().Return(errTestFlush).MinTimes(1) 707 c := mustNewTestTCPClient(t, testOptions()) 708 c.writerMgr = writerMgr 709 require.Equal(t, errTestFlush, c.Flush()) 710 } 711 712 func TestTCPClientFlushSuccess(t *testing.T) { 713 ctrl := gomock.NewController(t) 714 defer ctrl.Finish() 715 716 writerMgr := NewMockinstanceWriterManager(ctrl) 717 writerMgr.EXPECT().Flush().Return(nil).MinTimes(1) 718 c := mustNewTestTCPClient(t, testOptions()) 719 c.writerMgr = writerMgr 720 require.NoError(t, c.Flush()) 721 } 722 723 func TestTCPClientClosed(t *testing.T) { 724 c := mustNewTestTCPClient(t, testOptions()) 725 726 require.NoError(t, c.Close()) 727 require.Equal(t, errInstanceWriterManagerClosed, c.Close()) 728 } 729 730 func TestTCPClientCloseSuccess(t *testing.T) { 731 c := mustNewTestTCPClient(t, testOptions()) 732 require.NoError(t, c.Close()) 733 } 734 735 func TestTCPClientWriteTimeRangeFor(t *testing.T) { 736 c := mustNewTestTCPClient(t, testOptions()) 737 testShard := shard.NewShard(0).SetState(shard.Initializing) 738 for _, input := range []struct { 739 cutoverNanos int64 740 cutoffNanos int64 741 expectedEarliest int64 742 expectedLatest int64 743 }{ 744 { 745 cutoverNanos: 0, 746 cutoffNanos: int64(math.MaxInt64), 747 expectedEarliest: 0, 748 expectedLatest: int64(math.MaxInt64), 749 }, 750 { 751 cutoverNanos: testNowNanos, 752 cutoffNanos: int64(math.MaxInt64), 753 expectedEarliest: testNowNanos - int64(time.Minute), 754 expectedLatest: int64(math.MaxInt64), 755 }, 756 { 757 cutoverNanos: 0, 758 cutoffNanos: testNowNanos, 759 expectedEarliest: 0, 760 expectedLatest: testNowNanos + int64(10*time.Minute), 761 }, 762 } { 763 testShard = testShard.SetCutoverNanos(input.cutoverNanos).SetCutoffNanos(input.cutoffNanos) 764 earliest, latest := c.writeTimeRangeFor(testShard) 765 require.Equal(t, input.expectedEarliest, earliest) 766 require.Equal(t, input.expectedLatest, latest) 767 } 768 } 769 770 func TestTCPClientActivePlacement(t *testing.T) { 771 var ( 772 c = mustNewTestTCPClient(t, testOptions()) 773 emptyPl = placement.NewPlacement() 774 ctrl = gomock.NewController(t) 775 mockPl = placement.NewMockPlacement(ctrl) 776 watcher = placement.NewMockWatcher(ctrl) 777 ) 778 779 c.placementWatcher = watcher 780 watcher.EXPECT().Get().Return(mockPl, nil).Times(2) 781 mockPl.EXPECT().Version().Return(42).Times(2) 782 mockPl.EXPECT().Clone().Return(emptyPl) 783 784 pl, v, err := c.ActivePlacement() 785 assert.NoError(t, err) 786 assert.Equal(t, 42, v) 787 assert.Equal(t, emptyPl, pl) 788 789 v, err = c.ActivePlacementVersion() 790 assert.NoError(t, err) 791 assert.Equal(t, 42, v) 792 } 793 794 func TestTCPClientInitAndClose(t *testing.T) { 795 c := mustNewTestTCPClient(t, testOptions()) 796 require.NoError(t, c.Init()) 797 require.NoError(t, c.Close()) 798 } 799 800 func mustNewTestTCPClient(t *testing.T, opts Options) *TCPClient { 801 c, err := NewClient(opts) 802 require.NoError(t, err) 803 value, ok := c.(*TCPClient) 804 require.True(t, ok) 805 return value 806 } 807 808 // TODO: clean this up as it's in use by other test files 809 func testOptions() Options { 810 return testTCPClientOptions() 811 } 812 813 func testTCPClientOptions() Options { 814 const placementKey = "placement" 815 pl, err := placement.NewPlacement().Proto() 816 if err != nil { 817 panic(err.Error()) 818 } 819 820 plSnapshots := &placementpb.PlacementSnapshots{ 821 Snapshots: []*placementpb.Placement{ 822 pl, 823 }, 824 } 825 826 store := mem.NewStore() 827 if _, err := store.Set(placementKey, plSnapshots); err != nil { 828 panic(err.Error()) 829 } 830 831 plOpts := placement.NewWatcherOptions(). 832 SetStagedPlacementStore(store). 833 SetStagedPlacementKey(placementKey). 834 SetInitWatchTimeout(time.Millisecond) 835 return NewOptions(). 836 SetClockOptions(clock.NewOptions()). 837 SetConnectionOptions(testConnectionOptions()). 838 SetInstrumentOptions(instrument.NewOptions()). 839 SetShardFn(func([]byte, uint32) uint32 { return 1 }). 840 SetInstanceQueueSize(10). 841 SetMaxTimerBatchSize(140). 842 SetShardCutoverWarmupDuration(time.Minute). 843 SetShardCutoffLingerDuration(10 * time.Minute). 844 SetAggregatorClientType(TCPAggregatorClient). 845 SetWatcherOptions(plOpts). 846 SetForceFlushEvery(0). 847 SetFlushWorkerCount(8) 848 }