github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/msg/producer/writer/message_writer_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 package writer 22 23 import ( 24 "net" 25 "sync" 26 "testing" 27 "time" 28 29 "github.com/fortytw2/leaktest" 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/require" 32 "github.com/uber-go/tally" 33 34 "github.com/m3db/m3/src/msg/producer" 35 "github.com/m3db/m3/src/x/instrument" 36 "github.com/m3db/m3/src/x/retry" 37 xtest "github.com/m3db/m3/src/x/test" 38 ) 39 40 func TestMessageWriterRandomIndex(t *testing.T) { 41 indexes := make([]int, 10) 42 reset := func() { 43 for i := range indexes { 44 indexes[i] = i 45 } 46 } 47 48 reset() 49 firstIdx := randIndex(indexes, len(indexes)-1) 50 for { 51 reset() 52 newIdx := randIndex(indexes, len(indexes)-1) 53 // Make sure the first index is random. 54 if firstIdx != newIdx { 55 break 56 } 57 time.Sleep(50 * time.Millisecond) 58 } 59 60 reset() 61 idx1 := randIndex(indexes, len(indexes)-1) 62 idx2 := randIndex(indexes, len(indexes)-2) 63 for { 64 reset() 65 newIdx1 := randIndex(indexes, len(indexes)-1) 66 newIdx2 := randIndex(indexes, len(indexes)-2) 67 // Make sure the order is random. 68 if idx2-idx1 != newIdx2-newIdx1 { 69 break 70 } 71 time.Sleep(50 * time.Millisecond) 72 } 73 } 74 75 func TestMessageWriterRandomFullIteration(t *testing.T) { 76 indexes := make([]int, 100) 77 var indexMap map[int]struct{} 78 reset := func() { 79 for i := range indexes { 80 indexes[i] = i 81 } 82 indexMap = make(map[int]struct{}, 100) 83 } 84 85 for n := 0; n < 1000; n++ { 86 reset() 87 for i := len(indexes) - 1; i >= 0; i-- { 88 idx := randIndex(indexes, i) 89 indexMap[idx] = struct{}{} 90 } 91 require.Equal(t, 100, len(indexMap)) 92 } 93 } 94 95 func TestMessageWriter(t *testing.T) { 96 defer leaktest.Check(t)() 97 98 lis, err := net.Listen("tcp", "127.0.0.1:0") 99 require.NoError(t, err) 100 defer lis.Close() 101 102 addr := lis.Addr().String() 103 opts := testOptions() 104 105 var wg sync.WaitGroup 106 defer wg.Wait() 107 108 wg.Add(1) 109 go func() { 110 testConsumeAndAckOnConnectionListener(t, lis, opts.EncoderOptions(), opts.DecoderOptions()) 111 wg.Done() 112 }() 113 114 w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics()) 115 require.Equal(t, 200, int(w.ReplicatedShardID())) 116 w.Init() 117 118 a := newAckRouter(1) 119 a.Register(200, w) 120 121 cw := newConsumerWriter(addr, a, opts, testConsumerWriterMetrics()) 122 cw.Init() 123 defer cw.Close() 124 125 w.AddConsumerWriter(cw) 126 127 ctrl := xtest.NewController(t) 128 defer ctrl.Finish() 129 130 mm1 := producer.NewMockMessage(ctrl) 131 mm1.EXPECT().Bytes().Return([]byte("foo")).Times(1) 132 mm1.EXPECT().Size().Return(3).Times(1) 133 mm1.EXPECT().Finalize(producer.Consumed) 134 135 w.Write(producer.NewRefCountedMessage(mm1, nil)) 136 137 for { 138 w.RLock() 139 l := w.queue.Len() 140 w.RUnlock() 141 if l == 0 { 142 break 143 } 144 time.Sleep(100 * time.Millisecond) 145 } 146 require.Equal(t, 0, w.queue.Len()) 147 w.RemoveConsumerWriter(addr) 148 149 mm2 := producer.NewMockMessage(ctrl) 150 mm2.EXPECT().Bytes().Return([]byte("bar")).Times(1) 151 mm2.EXPECT().Size().Return(3).Times(1) 152 153 w.Write(producer.NewRefCountedMessage(mm2, nil)) 154 for { 155 if !isEmptyWithLock(w.acks) { 156 break 157 } 158 time.Sleep(100 * time.Millisecond) 159 } 160 require.Equal(t, 1, w.queue.Len()) 161 162 mm2.EXPECT().Finalize(producer.Consumed) 163 w.Ack(metadata{metadataKey: metadataKey{shard: 200, id: 2}}) 164 require.True(t, isEmptyWithLock(w.acks)) 165 for { 166 w.RLock() 167 l := w.queue.Len() 168 w.RUnlock() 169 if l == 0 { 170 break 171 } 172 time.Sleep(100 * time.Millisecond) 173 } 174 w.Close() 175 w.Close() 176 } 177 178 func TestMessageWriterRetry(t *testing.T) { 179 defer leaktest.Check(t)() 180 181 lis, err := net.Listen("tcp", "127.0.0.1:0") 182 require.NoError(t, err) 183 defer lis.Close() 184 185 addr := lis.Addr().String() 186 opts := testOptions() 187 w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics()) 188 w.Init() 189 defer w.Close() 190 191 a := newAckRouter(1) 192 a.Register(200, w) 193 194 ctrl := xtest.NewController(t) 195 defer ctrl.Finish() 196 197 mm := producer.NewMockMessage(ctrl) 198 mm.EXPECT().Bytes().Return([]byte("foo")).AnyTimes() 199 mm.EXPECT().Size().Return(3).Times(1) 200 mm.EXPECT().Finalize(producer.Consumed) 201 202 rm := producer.NewRefCountedMessage(mm, nil) 203 w.Write(rm) 204 205 w.AddConsumerWriter(newConsumerWriter("bad", a, opts, testConsumerWriterMetrics())) 206 require.Equal(t, 1, w.queue.Len()) 207 208 for { 209 if !isEmptyWithLock(w.acks) { 210 break 211 } 212 time.Sleep(100 * time.Millisecond) 213 } 214 215 require.Equal(t, 1, w.acks.size()) 216 w.acks.mtx.Lock() 217 _, ok := w.acks.acks[uint64(1)] 218 require.True(t, ok) 219 w.acks.mtx.Unlock() 220 221 cw := newConsumerWriter(addr, a, opts, testConsumerWriterMetrics()) 222 cw.Init() 223 defer cw.Close() 224 225 w.AddConsumerWriter(cw) 226 go func() { 227 testConsumeAndAckOnConnectionListener(t, lis, opts.EncoderOptions(), opts.DecoderOptions()) 228 }() 229 230 for { 231 w.Lock() 232 l := w.queue.Len() 233 w.Unlock() 234 if l == 0 { 235 break 236 } 237 time.Sleep(100 * time.Millisecond) 238 } 239 } 240 241 func TestMessageWriterCleanupDroppedMessage(t *testing.T) { 242 defer leaktest.Check(t)() 243 244 opts := testOptions() 245 w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics()) 246 247 ctrl := xtest.NewController(t) 248 defer ctrl.Finish() 249 250 mm := producer.NewMockMessage(ctrl) 251 252 mm.EXPECT().Size().Return(3).Times(1) 253 rm := producer.NewRefCountedMessage(mm, nil) 254 mm.EXPECT().Finalize(producer.Dropped) 255 rm.Drop() 256 mm.EXPECT().Bytes().Return([]byte("foo")) 257 w.Write(rm) 258 259 // A get will allocate a new message because the old one has not been returned to pool yet. 260 m := w.mPool.Get() 261 require.Nil(t, m.RefCountedMessage) 262 263 require.Equal(t, 1, w.queue.Len()) 264 w.Init() 265 defer w.Close() 266 267 for { 268 w.Lock() 269 l := w.queue.Len() 270 w.Unlock() 271 if l != 1 { 272 break 273 } 274 time.Sleep(100 * time.Millisecond) 275 } 276 require.True(t, isEmptyWithLock(w.acks)) 277 } 278 279 func TestMessageWriterCleanupAckedMessage(t *testing.T) { 280 defer leaktest.Check(t)() 281 282 opts := testOptions() 283 w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics()) 284 w.Init() 285 defer w.Close() 286 287 ctrl := xtest.NewController(t) 288 defer ctrl.Finish() 289 290 mm := producer.NewMockMessage(ctrl) 291 mm.EXPECT().Bytes().Return([]byte("foo")) 292 mm.EXPECT().Size().Return(3).Times(1) 293 294 rm := producer.NewRefCountedMessage(mm, nil) 295 // Another message write also holds this message. 296 rm.IncRef() 297 298 w.Write(rm) 299 for { 300 if !isEmptyWithLock(w.acks) { 301 break 302 } 303 time.Sleep(100 * time.Millisecond) 304 } 305 acks := w.acks 306 meta := metadata{ 307 metadataKey: metadataKey{ 308 id: 1, 309 shard: 200, 310 }, 311 } 312 // The message will not be finalized because it's still being hold by another message writer. 313 acks.ack(meta) 314 require.True(t, isEmptyWithLock(w.acks)) 315 316 // A get will allocate a new message because the old one has not been returned to pool yet. 317 m := w.mPool.Get() 318 require.Nil(t, m.RefCountedMessage) 319 require.Equal(t, 1, w.queue.Len()) 320 321 for { 322 w.Lock() 323 l := w.queue.Len() 324 w.Unlock() 325 if l != 1 { 326 break 327 } 328 time.Sleep(100 * time.Millisecond) 329 } 330 } 331 332 func TestMessageWriterCutoverCutoff(t *testing.T) { 333 ctrl := xtest.NewController(t) 334 defer ctrl.Finish() 335 336 w := newMessageWriter(200, newMessagePool(), nil, testMessageWriterMetrics()) 337 now := time.Now() 338 w.nowFn = func() time.Time { return now } 339 met := w.Metrics() 340 require.True(t, w.isValidWriteWithLock(now.UnixNano(), met)) 341 require.True(t, w.isValidWriteWithLock(now.UnixNano()+150, met)) 342 require.True(t, w.isValidWriteWithLock(now.UnixNano()+250, met)) 343 require.True(t, w.isValidWriteWithLock(now.UnixNano()+50, met)) 344 345 w.SetCutoffNanos(now.UnixNano() + 200) 346 w.SetCutoverNanos(now.UnixNano() + 100) 347 require.True(t, w.isValidWriteWithLock(now.UnixNano()+150, met)) 348 require.False(t, w.isValidWriteWithLock(now.UnixNano()+250, met)) 349 require.False(t, w.isValidWriteWithLock(now.UnixNano()+50, met)) 350 require.Equal(t, 0, w.queue.Len()) 351 352 mm := producer.NewMockMessage(ctrl) 353 mm.EXPECT().Size().Return(3) 354 w.Write(producer.NewRefCountedMessage(mm, nil)) 355 require.Equal(t, 0, w.queue.Len()) 356 } 357 358 func TestMessageWriterIgnoreCutoverCutoff(t *testing.T) { 359 ctrl := xtest.NewController(t) 360 defer ctrl.Finish() 361 362 opts := NewOptions().SetIgnoreCutoffCutover(true) 363 364 w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics()) 365 now := time.Now() 366 w.nowFn = func() time.Time { return now } 367 368 w.SetCutoffNanos(now.UnixNano() + 200) 369 w.SetCutoverNanos(now.UnixNano() + 100) 370 met := w.Metrics() 371 require.True(t, w.isValidWriteWithLock(now.UnixNano()+150, met)) 372 require.True(t, w.isValidWriteWithLock(now.UnixNano()+250, met)) 373 require.True(t, w.isValidWriteWithLock(now.UnixNano()+50, met)) 374 require.Equal(t, 0, w.queue.Len()) 375 376 mm := producer.NewMockMessage(ctrl) 377 mm.EXPECT().Bytes().Return([]byte("foo")) 378 mm.EXPECT().Size().Return(3) 379 w.Write(producer.NewRefCountedMessage(mm, nil)) 380 require.Equal(t, 1, w.queue.Len()) 381 } 382 383 func TestMessageWriterKeepNewWritesInOrderInFrontOfTheQueue(t *testing.T) { 384 ctrl := xtest.NewController(t) 385 defer ctrl.Finish() 386 387 opts := testOptions().SetMessageRetryNanosFn( 388 NextRetryNanosFn(retry.NewOptions().SetInitialBackoff(2 * time.Nanosecond).SetMaxBackoff(5 * time.Nanosecond)), 389 ) 390 w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics()) 391 392 now := time.Now() 393 w.nowFn = func() time.Time { return now } 394 395 mm1 := producer.NewMockMessage(ctrl) 396 mm1.EXPECT().Size().Return(3) 397 rm1 := producer.NewRefCountedMessage(mm1, nil) 398 mm1.EXPECT().Bytes().Return([]byte("1")).AnyTimes() 399 w.Write(rm1) 400 validateMessages(t, []*producer.RefCountedMessage{rm1}, w) 401 mm2 := producer.NewMockMessage(ctrl) 402 mm2.EXPECT().Size().Return(3) 403 rm2 := producer.NewRefCountedMessage(mm2, nil) 404 mm2.EXPECT().Bytes().Return([]byte("2")).AnyTimes() 405 w.Write(rm2) 406 validateMessages(t, []*producer.RefCountedMessage{rm1, rm2}, w) 407 w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), 2, true, &scanBatchMetrics{}) 408 409 w.lastNewWrite = nil 410 mm3 := producer.NewMockMessage(ctrl) 411 mm3.EXPECT().Size().Return(3) 412 rm3 := producer.NewRefCountedMessage(mm3, nil) 413 mm3.EXPECT().Bytes().Return([]byte("3")).AnyTimes() 414 w.Write(rm3) 415 validateMessages(t, []*producer.RefCountedMessage{rm3, rm1, rm2}, w) 416 417 mm4 := producer.NewMockMessage(ctrl) 418 mm4.EXPECT().Size().Return(3) 419 rm4 := producer.NewRefCountedMessage(mm4, nil) 420 mm4.EXPECT().Bytes().Return([]byte("4")).AnyTimes() 421 w.Write(rm4) 422 423 validateMessages(t, []*producer.RefCountedMessage{rm3, rm4, rm1, rm2}, w) 424 } 425 426 func TestMessageWriterRetryIterateBatchFullScan(t *testing.T) { 427 ctrl := xtest.NewController(t) 428 defer ctrl.Finish() 429 430 retryBatchSize := 2 431 opts := testOptions().SetMessageQueueScanBatchSize(retryBatchSize).SetMessageRetryNanosFn( 432 NextRetryNanosFn(retry.NewOptions().SetInitialBackoff(2 * time.Nanosecond).SetMaxBackoff(5 * time.Nanosecond)), 433 ) 434 w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics()) 435 436 now := time.Now() 437 w.nowFn = func() time.Time { return now } 438 439 mm1 := producer.NewMockMessage(ctrl) 440 mm1.EXPECT().Size().Return(3) 441 rm1 := producer.NewRefCountedMessage(mm1, nil) 442 mm1.EXPECT().Bytes().Return([]byte("1")).AnyTimes() 443 w.Write(rm1) 444 445 mm2 := producer.NewMockMessage(ctrl) 446 mm2.EXPECT().Size().Return(3) 447 rm2 := producer.NewRefCountedMessage(mm2, nil) 448 mm2.EXPECT().Bytes().Return([]byte("2")).AnyTimes() 449 w.Write(rm2) 450 451 mm3 := producer.NewMockMessage(ctrl) 452 mm3.EXPECT().Size().Return(3) 453 rm3 := producer.NewRefCountedMessage(mm3, nil) 454 mm3.EXPECT().Bytes().Return([]byte("3")).AnyTimes() 455 w.Write(rm3) 456 457 mm4 := producer.NewMockMessage(ctrl) 458 mm4.EXPECT().Size().Return(3) 459 rm4 := producer.NewRefCountedMessage(mm4, nil) 460 mm4.EXPECT().Bytes().Return([]byte("4")).AnyTimes() 461 w.Write(rm4) 462 463 validateMessages(t, []*producer.RefCountedMessage{rm1, rm2, rm3, rm4}, w) 464 mm1.EXPECT().Finalize(gomock.Eq(producer.Dropped)) 465 rm1.Drop() 466 require.Equal(t, 4, w.queue.Len()) 467 e, toBeRetried := w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), retryBatchSize, true, &scanBatchMetrics{}) 468 require.Equal(t, 1, len(toBeRetried)) 469 require.Equal(t, 3, w.queue.Len()) 470 471 // Make sure it stopped at rm3. 472 require.Equal(t, rm3, e.Value.(*message).RefCountedMessage) 473 474 require.Equal(t, 3, w.queue.Len()) 475 e, toBeRetried = w.scanBatchWithLock(e, w.nowFn().UnixNano(), retryBatchSize, true, &scanBatchMetrics{}) 476 require.Nil(t, e) 477 require.Equal(t, 2, len(toBeRetried)) 478 require.Equal(t, 3, w.queue.Len()) 479 480 e, toBeRetried = w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), retryBatchSize, true, &scanBatchMetrics{}) 481 // Make sure it stopped at rm4. 482 require.Equal(t, rm4, e.Value.(*message).RefCountedMessage) 483 require.Equal(t, 0, len(toBeRetried)) 484 485 e, toBeRetried = w.scanBatchWithLock(e, w.nowFn().UnixNano(), retryBatchSize, true, &scanBatchMetrics{}) 486 require.Nil(t, e) 487 require.Equal(t, 0, len(toBeRetried)) 488 } 489 490 //nolint:lll 491 func TestMessageWriterRetryIterateBatchFullScanWithMessageTTL(t *testing.T) { 492 ctrl := xtest.NewController(t) 493 defer ctrl.Finish() 494 495 retryBatchSize := 2 496 opts := testOptions().SetMessageQueueScanBatchSize(retryBatchSize).SetMessageRetryNanosFn( 497 NextRetryNanosFn(retry.NewOptions().SetInitialBackoff(2 * time.Nanosecond).SetMaxBackoff(5 * time.Nanosecond)), 498 ) 499 w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics()) 500 501 now := time.Now() 502 w.nowFn = func() time.Time { return now } 503 504 mm1 := producer.NewMockMessage(ctrl) 505 mm1.EXPECT().Size().Return(3) 506 rm1 := producer.NewRefCountedMessage(mm1, nil) 507 mm1.EXPECT().Bytes().Return([]byte("1")).AnyTimes() 508 w.Write(rm1) 509 510 mm2 := producer.NewMockMessage(ctrl) 511 mm2.EXPECT().Size().Return(3) 512 rm2 := producer.NewRefCountedMessage(mm2, nil) 513 mm2.EXPECT().Bytes().Return([]byte("2")).AnyTimes() 514 w.Write(rm2) 515 516 mm3 := producer.NewMockMessage(ctrl) 517 mm3.EXPECT().Size().Return(3) 518 rm3 := producer.NewRefCountedMessage(mm3, nil) 519 mm3.EXPECT().Bytes().Return([]byte("3")).AnyTimes() 520 w.Write(rm3) 521 522 mm4 := producer.NewMockMessage(ctrl) 523 mm4.EXPECT().Size().Return(3) 524 rm4 := producer.NewRefCountedMessage(mm4, nil) 525 mm4.EXPECT().Bytes().Return([]byte("4")).AnyTimes() 526 w.Write(rm4) 527 528 mm1.EXPECT().Finalize(gomock.Eq(producer.Dropped)) 529 rm1.Drop() 530 require.Equal(t, 4, w.queue.Len()) 531 e, toBeRetried := w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), retryBatchSize, true, &scanBatchMetrics{}) 532 require.Equal(t, 1, len(toBeRetried)) 533 require.Equal(t, 3, w.queue.Len()) 534 535 require.Equal(t, rm2, e.Value.(*message).RefCountedMessage) 536 537 w.SetMessageTTLNanos(int64(time.Minute)) 538 mm4.EXPECT().Finalize(gomock.Eq(producer.Consumed)) 539 mm3.EXPECT().Finalize(gomock.Eq(producer.Consumed)) 540 e, toBeRetried = w.scanBatchWithLock(e, w.nowFn().UnixNano()+int64(time.Hour), retryBatchSize, true, &scanBatchMetrics{}) 541 require.Equal(t, 0, len(toBeRetried)) 542 require.Equal(t, 1, w.queue.Len()) 543 require.Nil(t, e) 544 545 mm2.EXPECT().Finalize(gomock.Eq(producer.Consumed)) 546 e, toBeRetried = w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano()+int64(time.Hour), retryBatchSize, true, &scanBatchMetrics{}) 547 require.Equal(t, 0, len(toBeRetried)) 548 require.Equal(t, 0, w.queue.Len()) 549 require.Nil(t, e) 550 } 551 552 //nolint:lll 553 func TestMessageWriterRetryIterateBatchNotFullScan(t *testing.T) { 554 ctrl := xtest.NewController(t) 555 defer ctrl.Finish() 556 557 retryBatchSize := 100 558 opts := testOptions().SetMessageQueueScanBatchSize(retryBatchSize).SetMessageRetryNanosFn( 559 NextRetryNanosFn(retry.NewOptions().SetInitialBackoff(2 * time.Nanosecond).SetMaxBackoff(5 * time.Nanosecond)), 560 ) 561 w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics()) 562 563 now := time.Now() 564 w.nowFn = func() time.Time { return now } 565 566 mm1 := producer.NewMockMessage(ctrl) 567 mm1.EXPECT().Size().Return(1) 568 rm1 := producer.NewRefCountedMessage(mm1, nil) 569 mm1.EXPECT().Bytes().Return([]byte("1")).AnyTimes() 570 w.Write(rm1) 571 572 mm2 := producer.NewMockMessage(ctrl) 573 mm2.EXPECT().Size().Return(1) 574 rm2 := producer.NewRefCountedMessage(mm2, nil) 575 mm2.EXPECT().Bytes().Return([]byte("2")).AnyTimes() 576 w.Write(rm2) 577 578 mm3 := producer.NewMockMessage(ctrl) 579 mm3.EXPECT().Size().Return(1) 580 rm3 := producer.NewRefCountedMessage(mm3, nil) 581 mm3.EXPECT().Bytes().Return([]byte("3")).AnyTimes() 582 w.Write(rm3) 583 584 mm4 := producer.NewMockMessage(ctrl) 585 mm4.EXPECT().Size().Return(1) 586 rm4 := producer.NewRefCountedMessage(mm4, nil) 587 mm4.EXPECT().Bytes().Return([]byte("4")).AnyTimes() 588 w.Write(rm4) 589 590 mm1.EXPECT().Finalize(gomock.Eq(producer.Dropped)) 591 rm1.Drop() 592 require.Equal(t, 4, w.queue.Len()) 593 e, toBeRetried := w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), retryBatchSize, false, &scanBatchMetrics{}) 594 require.Equal(t, 3, len(toBeRetried)) 595 require.Equal(t, 3, w.queue.Len()) 596 require.Nil(t, e) 597 validateMessages(t, []*producer.RefCountedMessage{rm2, rm3, rm4}, w) 598 // Although mm4 is dropped, it will not be removed from the queue because 599 // it was not checked. 600 mm4.EXPECT().Finalize(gomock.Eq(producer.Dropped)) 601 rm4.Drop() 602 require.Equal(t, 3, w.queue.Len()) 603 e, toBeRetried = w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), retryBatchSize, false, &scanBatchMetrics{}) 604 require.Equal(t, rm2, e.Value.(*message).RefCountedMessage) 605 require.Equal(t, 0, len(toBeRetried)) 606 require.Equal(t, 3, w.queue.Len()) 607 require.Equal(t, rm2, e.Value.(*message).RefCountedMessage) 608 validateMessages(t, []*producer.RefCountedMessage{rm2, rm3, rm4}, w) 609 w.lastNewWrite = nil 610 611 mm5 := producer.NewMockMessage(ctrl) 612 mm5.EXPECT().Size().Return(1) 613 rm5 := producer.NewRefCountedMessage(mm5, nil) 614 mm5.EXPECT().Bytes().Return([]byte("5")).AnyTimes() 615 w.Write(rm5) 616 validateMessages(t, []*producer.RefCountedMessage{rm5, rm2, rm3, rm4}, w) 617 618 require.Equal(t, 4, w.queue.Len()) 619 e, toBeRetried = w.scanBatchWithLock(w.queue.Front(), w.nowFn().UnixNano(), retryBatchSize, false, &scanBatchMetrics{}) 620 require.Equal(t, rm2, e.Value.(*message).RefCountedMessage) 621 require.Equal(t, 1, len(toBeRetried)) 622 require.Equal(t, rm5, toBeRetried[0].RefCountedMessage) 623 require.Equal(t, 4, w.queue.Len()) 624 } 625 626 func TestNextRetryAfterNanos(t *testing.T) { 627 backoffDuration := time.Minute 628 opts := testOptions(). 629 SetMessageRetryNanosFn( 630 NextRetryNanosFn( 631 retry.NewOptions(). 632 SetInitialBackoff(backoffDuration). 633 SetMaxBackoff(2 * backoffDuration). 634 SetJitter(true), 635 ), 636 ) 637 w := newMessageWriter(200, nil, opts, testMessageWriterMetrics()) 638 639 nowNanos := time.Now().UnixNano() 640 m := newMessage() 641 m.IncWriteTimes() 642 retryAtNanos := w.nextRetryAfterNanos(m.WriteTimes()) + nowNanos 643 require.True(t, retryAtNanos > nowNanos) 644 require.True(t, retryAtNanos < nowNanos+int64(backoffDuration)) 645 646 m.IncWriteTimes() 647 retryAtNanos = w.nextRetryAfterNanos(m.WriteTimes()) + nowNanos 648 require.True(t, retryAtNanos >= nowNanos+int64(backoffDuration)) 649 require.True(t, retryAtNanos < nowNanos+2*int64(backoffDuration)) 650 651 m.IncWriteTimes() 652 retryAtNanos = w.nextRetryAfterNanos(m.WriteTimes()) + nowNanos 653 require.True(t, retryAtNanos == nowNanos+2*int64(backoffDuration)) 654 } 655 656 func TestStaticRetryAfterNanos(t *testing.T) { 657 fn, err := StaticRetryNanosFn([]time.Duration{time.Minute, 10 * time.Second, 5 * time.Second}) 658 require.NoError(t, err) 659 660 opts := testOptions().SetMessageRetryNanosFn(fn) 661 w := newMessageWriter(200, nil, opts, testMessageWriterMetrics()) 662 663 m := newMessage() 664 m.IncWriteTimes() 665 retryAtNanos := w.nextRetryAfterNanos(m.WriteTimes()) 666 require.Equal(t, int64(time.Minute), retryAtNanos) 667 668 m.IncWriteTimes() 669 retryAtNanos = w.nextRetryAfterNanos(m.WriteTimes()) 670 require.Equal(t, int64(10*time.Second), retryAtNanos) 671 672 m.IncWriteTimes() 673 retryAtNanos = w.nextRetryAfterNanos(m.WriteTimes()) 674 require.Equal(t, int64(5*time.Second), retryAtNanos) 675 676 m.IncWriteTimes() 677 retryAtNanos = w.nextRetryAfterNanos(m.WriteTimes()) 678 require.Equal(t, int64(5*time.Second), retryAtNanos) 679 } 680 681 func TestExpectedProcessedAt(t *testing.T) { 682 m := newMessage() 683 m.initNanos = 100 684 m.SetRetryAtNanos(200) 685 require.Equal(t, int64(100), m.ExpectedProcessAtNanos()) 686 m.SetRetryAtNanos(300) 687 require.Equal(t, int64(200), m.ExpectedProcessAtNanos()) 688 } 689 690 func TestMessageWriterCloseCleanupAllMessages(t *testing.T) { 691 defer leaktest.Check(t)() 692 693 opts := testOptions() 694 w := newMessageWriter(200, newMessagePool(), opts, testMessageWriterMetrics()) 695 696 ctrl := xtest.NewController(t) 697 defer ctrl.Finish() 698 699 mm := producer.NewMockMessage(ctrl) 700 mm.EXPECT().Size().Return(3) 701 702 rm := producer.NewRefCountedMessage(mm, nil) 703 mm.EXPECT().Finalize(producer.Consumed) 704 mm.EXPECT().Bytes().Return([]byte("foo")) 705 w.Write(rm) 706 require.False(t, isEmptyWithLock(w.acks)) 707 require.Equal(t, 1, w.queue.Len()) 708 w.Init() 709 w.Close() 710 require.Equal(t, 0, w.queue.Len()) 711 require.True(t, isEmptyWithLock(w.acks)) 712 } 713 714 func TestMessageWriterQueueFullScanOnWriteErrors(t *testing.T) { 715 ctrl := xtest.NewController(t) 716 defer ctrl.Finish() 717 718 opts := testOptions().SetMessageQueueScanBatchSize(1) 719 scope := tally.NewTestScope("", nil) 720 metrics := testMessageWriterMetricsWithScope(scope).withConsumer("c1") 721 w := newMessageWriter(200, newMessagePool(), opts, metrics) 722 w.AddConsumerWriter(newConsumerWriter("bad", nil, opts, testConsumerWriterMetrics())) 723 724 mm1 := producer.NewMockMessage(ctrl) 725 mm1.EXPECT().Size().Return(3) 726 mm1.EXPECT().Bytes().Return([]byte("foo")) 727 rm1 := producer.NewRefCountedMessage(mm1, nil) 728 w.Write(rm1) 729 require.Equal(t, 1, w.queue.Len()) 730 731 mm2 := producer.NewMockMessage(ctrl) 732 mm2.EXPECT().Size().Return(3) 733 mm2.EXPECT().Bytes().Return([]byte("foo")) 734 rm2 := producer.NewRefCountedMessage(mm2, nil) 735 w.Write(rm2) 736 require.Equal(t, 2, w.queue.Len()) 737 738 mm1.EXPECT().Finalize(producer.Dropped) 739 rm1.Drop() 740 w.scanMessageQueue() 741 require.Equal(t, 1, w.queue.Len()) 742 743 snapshot := scope.Snapshot() 744 counters := snapshot.Counters() 745 require.Equal(t, int64(1), counters["message-processed+consumer=c1,result=write"].Value()) 746 require.Equal(t, int64(1), counters["message-processed+consumer=c1,result=drop"].Value()) 747 } 748 749 func TestMessageWriter_WithoutConsumerScope(t *testing.T) { 750 ctrl := xtest.NewController(t) 751 defer ctrl.Finish() 752 753 opts := testOptions().SetMessageQueueScanBatchSize(1) 754 scope := tally.NewTestScope("", nil) 755 metrics := newMessageWriterMetrics(scope, instrument.TimerOptions{}, true) 756 w := newMessageWriter(200, nil, opts, metrics) 757 w.AddConsumerWriter(newConsumerWriter("bad", nil, opts, testConsumerWriterMetrics())) 758 759 snapshot := scope.Snapshot() 760 counters := snapshot.Counters() 761 require.Nil(t, counters["message-processed+consumer=c1,result=write"]) 762 require.NotNil(t, counters["message-processed+result=write"]) 763 } 764 765 func isEmptyWithLock(h *acks) bool { 766 return h.size() == 0 767 } 768 769 func testMessageWriterMetrics() *messageWriterMetrics { 770 return newMessageWriterMetrics(tally.NoopScope, instrument.TimerOptions{}, false) 771 } 772 773 func testMessageWriterMetricsWithScope(scope tally.TestScope) *messageWriterMetrics { 774 return newMessageWriterMetrics(scope, instrument.TimerOptions{}, false) 775 } 776 777 func validateMessages(t *testing.T, msgs []*producer.RefCountedMessage, w *messageWriter) { 778 w.RLock() 779 idx := 0 780 for e := w.queue.Front(); e != nil; e = e.Next() { 781 require.Equal(t, msgs[idx].Bytes(), e.Value.(*message).RefCountedMessage.Bytes()) 782 idx++ 783 } 784 w.RUnlock() 785 require.Equal(t, idx, len(msgs)) 786 }