github.com/Jeffail/benthos/v3@v3.65.0/lib/input/async_reader_test.go (about) 1 package input 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 "reflect" 9 "runtime/pprof" 10 "sync" 11 "testing" 12 "time" 13 14 "github.com/Jeffail/benthos/v3/lib/input/reader" 15 "github.com/Jeffail/benthos/v3/lib/log" 16 "github.com/Jeffail/benthos/v3/lib/message" 17 "github.com/Jeffail/benthos/v3/lib/metrics" 18 "github.com/Jeffail/benthos/v3/lib/response" 19 "github.com/Jeffail/benthos/v3/lib/types" 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 ) 23 24 //------------------------------------------------------------------------------ 25 26 type mockAsyncReader struct { 27 msgsToSnd []types.Message 28 ackRcvd []error 29 ackMut sync.Mutex 30 31 connChan chan error 32 readChan chan error 33 ackChan chan error 34 closeAsyncChan chan struct{} 35 closeAsyncOnce sync.Once 36 } 37 38 func newMockAsyncReader() *mockAsyncReader { 39 return &mockAsyncReader{ 40 connChan: make(chan error), 41 readChan: make(chan error), 42 ackChan: make(chan error), 43 closeAsyncChan: make(chan struct{}), 44 } 45 } 46 47 func (r *mockAsyncReader) ConnectWithContext(ctx context.Context) error { 48 cerr, open := <-r.connChan 49 if !open { 50 return types.ErrNotConnected 51 } 52 return cerr 53 } 54 55 func (r *mockAsyncReader) ReadWithContext(ctx context.Context) (types.Message, reader.AsyncAckFn, error) { 56 select { 57 case <-ctx.Done(): 58 return nil, nil, types.ErrTimeout 59 case err, open := <-r.readChan: 60 if !open { 61 return nil, nil, types.ErrNotConnected 62 } 63 if err != nil { 64 return nil, nil, err 65 } 66 } 67 r.ackMut.Lock() 68 r.ackRcvd = append(r.ackRcvd, errors.New("ack not received")) 69 i := len(r.ackRcvd) - 1 70 r.ackMut.Unlock() 71 72 var nextMsg types.Message = message.New(nil) 73 if len(r.msgsToSnd) > 0 { 74 nextMsg = r.msgsToSnd[0] 75 r.msgsToSnd = r.msgsToSnd[1:] 76 } 77 78 return nextMsg.DeepCopy(), func(ctx context.Context, res types.Response) error { 79 if res.SkipAck() { 80 return nil 81 } 82 r.ackMut.Lock() 83 r.ackRcvd[i] = res.Error() 84 r.ackMut.Unlock() 85 select { 86 case err := <-r.ackChan: 87 return err 88 case <-ctx.Done(): 89 } 90 return nil 91 }, nil 92 } 93 94 func (r *mockAsyncReader) CloseAsync() { 95 r.closeAsyncOnce.Do(func() { 96 close(r.closeAsyncChan) 97 }) 98 } 99 100 func (r *mockAsyncReader) WaitForClose(time.Duration) error { 101 return nil 102 } 103 104 //------------------------------------------------------------------------------ 105 106 type asyncReaderCantConnect struct{} 107 108 func (r asyncReaderCantConnect) ConnectWithContext(ctx context.Context) error { 109 return types.ErrNotConnected 110 } 111 func (r asyncReaderCantConnect) ReadWithContext(ctx context.Context) (types.Message, reader.AsyncAckFn, error) { 112 return nil, nil, types.ErrNotConnected 113 } 114 func (r asyncReaderCantConnect) CloseAsync() {} 115 func (r asyncReaderCantConnect) WaitForClose(time.Duration) error { 116 return nil 117 } 118 119 func TestAsyncReaderCantConnect(t *testing.T) { 120 r, err := NewAsyncReader( 121 "foo", true, asyncReaderCantConnect{}, 122 log.Noop(), metrics.Noop(), 123 ) 124 if err != nil { 125 t.Error(err) 126 return 127 } 128 129 // We will fail to connect but should still exit immediately. 130 r.CloseAsync() 131 if err = r.WaitForClose(time.Second); err != nil { 132 t.Error(err) 133 } 134 } 135 136 //------------------------------------------------------------------------------ 137 138 type asyncReaderCantRead struct { 139 connected int 140 } 141 142 func (r *asyncReaderCantRead) ConnectWithContext(ctx context.Context) error { 143 r.connected++ 144 return nil 145 } 146 func (r *asyncReaderCantRead) ReadWithContext(ctx context.Context) (types.Message, reader.AsyncAckFn, error) { 147 return nil, nil, types.ErrNotConnected 148 } 149 func (r *asyncReaderCantRead) CloseAsync() {} 150 func (r *asyncReaderCantRead) WaitForClose(time.Duration) error { 151 return nil 152 } 153 154 func TestAsyncReaderCantRead(t *testing.T) { 155 readerImpl := &asyncReaderCantRead{} 156 157 r, err := NewAsyncReader( 158 "foo", true, readerImpl, 159 log.Noop(), metrics.Noop(), 160 ) 161 if err != nil { 162 t.Error(err) 163 return 164 } 165 166 // We will be failing to send but should still exit immediately. 167 r.CloseAsync() 168 if err = r.WaitForClose(time.Second); err != nil { 169 t.Error(err) 170 } 171 172 if readerImpl.connected < 1 { 173 t.Errorf("Connected wasn't called enough times: %v", readerImpl.connected) 174 } 175 } 176 177 //------------------------------------------------------------------------------ 178 179 func TestAsyncReaderTypeClosedOnConn(t *testing.T) { 180 readerImpl := newMockAsyncReader() 181 182 r, err := NewAsyncReader( 183 "foo", true, readerImpl, 184 log.Noop(), metrics.Noop(), 185 ) 186 if err != nil { 187 t.Error(err) 188 return 189 } 190 191 go func() { 192 select { 193 case readerImpl.connChan <- types.ErrTypeClosed: 194 case <-time.After(time.Second): 195 } 196 }() 197 198 if err = r.WaitForClose(time.Second); err != nil { 199 t.Error(err) 200 } 201 } 202 203 func TestAsyncReaderTypeClosedOnReconn(t *testing.T) { 204 readerImpl := newMockAsyncReader() 205 206 r, err := NewAsyncReader( 207 "foo", true, readerImpl, 208 log.Noop(), metrics.Noop(), 209 ) 210 if err != nil { 211 t.Error(err) 212 return 213 } 214 215 go func() { 216 select { 217 case readerImpl.connChan <- nil: 218 case <-time.After(time.Second): 219 } 220 select { 221 case readerImpl.readChan <- types.ErrNotConnected: 222 case <-time.After(time.Second): 223 } 224 select { 225 case readerImpl.connChan <- types.ErrTypeClosed: 226 case <-time.After(time.Second): 227 } 228 }() 229 230 if err = r.WaitForClose(time.Second); err != nil { 231 t.Error(err) 232 } 233 } 234 235 func TestAsyncReaderTypeClosedOnReread(t *testing.T) { 236 readerImpl := newMockAsyncReader() 237 238 r, err := NewAsyncReader( 239 "foo", true, readerImpl, 240 log.Noop(), metrics.Noop(), 241 ) 242 if err != nil { 243 t.Fatal(err) 244 } 245 246 go func() { 247 select { 248 case readerImpl.connChan <- nil: 249 case <-time.After(time.Second): 250 } 251 select { 252 case readerImpl.readChan <- types.ErrNotConnected: 253 case <-time.After(time.Second): 254 } 255 select { 256 case readerImpl.connChan <- nil: 257 case <-time.After(time.Second): 258 } 259 select { 260 case readerImpl.readChan <- types.ErrTypeClosed: 261 case <-time.After(time.Second): 262 } 263 }() 264 265 if err = r.WaitForClose(time.Second); err != nil { 266 t.Error(err) 267 } 268 } 269 270 //------------------------------------------------------------------------------ 271 272 func TestAsyncReaderCanReconnect(t *testing.T) { 273 readerImpl := newMockAsyncReader() 274 275 r, err := NewAsyncReader( 276 "foo", true, readerImpl, 277 log.Noop(), metrics.Noop(), 278 ) 279 if err != nil { 280 t.Error(err) 281 return 282 } 283 284 go func() { 285 select { 286 case readerImpl.connChan <- nil: 287 case <-time.After(time.Second): 288 } 289 select { 290 case readerImpl.readChan <- types.ErrNotConnected: 291 case <-time.After(time.Second): 292 } 293 select { 294 case readerImpl.connChan <- nil: 295 case <-time.After(time.Second): 296 } 297 select { 298 case readerImpl.readChan <- nil: 299 case <-time.After(time.Second): 300 } 301 select { 302 case readerImpl.ackChan <- nil: 303 case <-time.After(time.Second): 304 } 305 }() 306 307 var ts types.Transaction 308 var open bool 309 select { 310 case ts, open = <-r.TransactionChan(): 311 if !open { 312 t.Fatal("Closed early") 313 } 314 case <-time.After(time.Second): 315 t.Error("Timed out") 316 } 317 318 select { 319 case ts.ResponseChan <- response.NewAck(): 320 case <-time.After(time.Second): 321 t.Error("Timed out") 322 } 323 324 // We will be failing to send but should still exit immediately. 325 r.CloseAsync() 326 327 go func() { 328 select { 329 case readerImpl.readChan <- nil: 330 case readerImpl.connChan <- types.ErrNotConnected: 331 case <-time.After(time.Second): 332 } 333 }() 334 335 if err = r.WaitForClose(time.Second); err != nil { 336 t.Error(err) 337 } 338 } 339 340 func TestAsyncReaderFailsReconnect(t *testing.T) { 341 readerImpl := newMockAsyncReader() 342 343 r, err := NewAsyncReader( 344 "foo", true, readerImpl, 345 log.Noop(), metrics.Noop(), 346 ) 347 if err != nil { 348 t.Error(err) 349 return 350 } 351 352 go func() { 353 select { 354 case readerImpl.connChan <- nil: 355 case <-time.After(time.Second): 356 } 357 select { 358 case readerImpl.readChan <- types.ErrNotConnected: 359 case <-time.After(time.Second): 360 } 361 select { 362 case readerImpl.connChan <- types.ErrNotConnected: 363 case <-time.After(time.Second): 364 } 365 select { 366 case readerImpl.connChan <- nil: 367 case <-time.After(time.Second * 2): 368 } 369 select { 370 case readerImpl.readChan <- nil: 371 case <-time.After(time.Second): 372 } 373 select { 374 case readerImpl.ackChan <- nil: 375 case <-time.After(time.Second): 376 } 377 }() 378 379 var ts types.Transaction 380 var open bool 381 select { 382 case ts, open = <-r.TransactionChan(): 383 if !open { 384 t.Fatal("Closed early") 385 } 386 case <-time.After(time.Second * 2): 387 t.Error("Timed out") 388 } 389 390 select { 391 case ts.ResponseChan <- response.NewAck(): 392 case <-time.After(time.Second): 393 t.Error("Timed out") 394 } 395 396 // We will be failing to send but should still exit immediately. 397 r.CloseAsync() 398 399 go func() { 400 select { 401 case readerImpl.readChan <- nil: 402 case <-time.After(time.Second): 403 } 404 }() 405 406 if err = r.WaitForClose(time.Second); err != nil { 407 t.Error(err) 408 } 409 } 410 411 func TestAsyncReaderCloseDuringReconnect(t *testing.T) { 412 readerImpl := newMockAsyncReader() 413 414 r, err := NewAsyncReader( 415 "foo", true, readerImpl, 416 log.Noop(), metrics.Noop(), 417 ) 418 if err != nil { 419 t.Fatal(err) 420 } 421 422 select { 423 case readerImpl.connChan <- nil: 424 case <-time.After(time.Second): 425 t.Fatal("Timed out") 426 } 427 select { 428 case readerImpl.readChan <- types.ErrNotConnected: 429 case <-time.After(time.Second): 430 t.Fatal("Timed out") 431 } 432 433 go func() { 434 select { 435 case readerImpl.connChan <- types.ErrNotConnected: 436 case <-time.After(time.Second): 437 } 438 close(readerImpl.connChan) 439 }() 440 441 // We will be failing to send but should still exit immediately. 442 r.CloseAsync() 443 close(readerImpl.readChan) 444 445 if err = r.WaitForClose(time.Second); err != nil { 446 pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 447 t.Error(err) 448 } 449 } 450 451 func TestAsyncReaderHappyPath(t *testing.T) { 452 exp := [][]byte{[]byte("foo"), []byte("bar")} 453 454 readerImpl := newMockAsyncReader() 455 readerImpl.msgsToSnd = []types.Message{message.New(exp)} 456 457 r, err := NewAsyncReader( 458 "foo", true, readerImpl, 459 log.Noop(), metrics.Noop(), 460 ) 461 if err != nil { 462 t.Fatal(err) 463 } 464 465 select { 466 case readerImpl.connChan <- nil: 467 case <-time.After(time.Second): 468 t.Fatal("Timed out") 469 } 470 471 go func() { 472 select { 473 case readerImpl.readChan <- nil: 474 case <-time.After(time.Second): 475 } 476 select { 477 case readerImpl.ackChan <- nil: 478 case <-time.After(time.Second): 479 } 480 }() 481 482 var ts types.Transaction 483 var open bool 484 485 select { 486 case ts, open = <-r.TransactionChan(): 487 if !open { 488 t.Fatal("Chan closed") 489 } 490 if act := message.GetAllBytes(ts.Payload); !reflect.DeepEqual(exp, act) { 491 t.Errorf("Wrong message returned: %v != %v", act, exp) 492 } 493 case <-time.After(time.Second): 494 t.Fatal("Timed out") 495 } 496 497 select { 498 case ts.ResponseChan <- response.NewAck(): 499 case <-time.After(time.Second): 500 t.Fatal("Timed out") 501 } 502 503 // We will be failing to send but should still exit immediately. 504 r.CloseAsync() 505 close(readerImpl.readChan) 506 close(readerImpl.connChan) 507 508 if err = r.WaitForClose(time.Second); err != nil { 509 pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 510 t.Fatal(err) 511 } 512 513 if readerImpl.ackRcvd[0] != nil { 514 t.Error(readerImpl.ackRcvd[0]) 515 } 516 } 517 518 func TestAsyncReaderCloseWithPendingAcks(t *testing.T) { 519 exp := [][]byte{[]byte("hello world")} 520 521 readerImpl := newMockAsyncReader() 522 readerImpl.msgsToSnd = []types.Message{message.New(exp)} 523 524 r, err := NewAsyncReader("foo", true, readerImpl, log.Noop(), metrics.Noop()) 525 require.NoError(t, err) 526 527 select { 528 case readerImpl.connChan <- nil: 529 case <-time.After(time.Second): 530 t.Fatal("Timed out") 531 } 532 533 go func() { 534 select { 535 case readerImpl.readChan <- nil: 536 case <-time.After(time.Second): 537 } 538 }() 539 540 var ts types.Transaction 541 var open bool 542 543 select { 544 case ts, open = <-r.TransactionChan(): 545 require.True(t, open) 546 assert.Equal(t, message.GetAllBytes(ts.Payload), exp) 547 case <-time.After(time.Second): 548 t.Fatal("Timed out") 549 } 550 551 select { 552 case ts.ResponseChan <- response.NewAck(): 553 case <-time.After(time.Second): 554 t.Fatal("Timed out") 555 } 556 557 // Blocking the reader ack for now 558 r.CloseAsync() 559 560 select { 561 case <-readerImpl.closeAsyncChan: 562 t.Fatal("reader closed early") 563 // case <-time.After(time.Millisecond * 100): 564 case <-time.After(time.Second): 565 } 566 567 select { 568 case readerImpl.ackChan <- nil: 569 case <-time.After(time.Second): 570 t.Fatal("Timed out") 571 } 572 573 select { 574 case <-readerImpl.closeAsyncChan: 575 case <-time.After(time.Second): 576 t.Fatal("Timed out") 577 } 578 579 if readerImpl.ackRcvd[0] != nil { 580 t.Error(readerImpl.ackRcvd[0]) 581 } 582 } 583 584 func TestAsyncReaderSadPath(t *testing.T) { 585 exp := [][]byte{[]byte("foo"), []byte("bar")} 586 expErr := errors.New("test error") 587 588 readerImpl := newMockAsyncReader() 589 readerImpl.msgsToSnd = []types.Message{message.New(exp)} 590 591 r, err := NewAsyncReader( 592 "foo", true, readerImpl, 593 log.Noop(), metrics.Noop(), 594 ) 595 if err != nil { 596 t.Fatal(err) 597 } 598 599 select { 600 case readerImpl.connChan <- nil: 601 case <-time.After(time.Second): 602 t.Fatal("Timed out") 603 } 604 605 go func() { 606 for { 607 select { 608 case readerImpl.readChan <- nil: 609 select { 610 case readerImpl.ackChan <- nil: 611 case <-time.After(time.Second): 612 } 613 return 614 case readerImpl.connChan <- nil: 615 case <-time.After(time.Second): 616 } 617 } 618 }() 619 620 var ts types.Transaction 621 var open bool 622 623 select { 624 case ts, open = <-r.TransactionChan(): 625 if !open { 626 t.Fatal("Chan closed") 627 } 628 if act := message.GetAllBytes(ts.Payload); !reflect.DeepEqual(exp, act) { 629 t.Errorf("Wrong message returned: %v != %v", act, exp) 630 } 631 case <-time.After(time.Second): 632 t.Fatal("Timed out") 633 } 634 635 select { 636 case ts.ResponseChan <- response.NewError(expErr): 637 case <-time.After(time.Second): 638 t.Fatal("Timed out") 639 } 640 641 // We will be failing to send but should still exit immediately. 642 r.CloseAsync() 643 close(readerImpl.readChan) 644 close(readerImpl.connChan) 645 646 if err = r.WaitForClose(time.Second); err != nil { 647 pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 648 t.Fatal(err) 649 } 650 651 if actErr := readerImpl.ackRcvd[0]; expErr != actErr { 652 t.Errorf("Wrong response received: %v != %v", actErr, expErr) 653 } 654 } 655 656 func TestAsyncReaderParallel(t *testing.T) { 657 expMsgs := []string{} 658 for i := 0; i < 10; i++ { 659 expMsgs = append(expMsgs, fmt.Sprintf("message: %v", i)) 660 } 661 readerImpl := newMockAsyncReader() 662 for _, str := range expMsgs { 663 readerImpl.msgsToSnd = append(readerImpl.msgsToSnd, message.New([][]byte{[]byte(str)})) 664 } 665 666 r, err := NewAsyncReader( 667 "foo", true, readerImpl, 668 log.Noop(), metrics.Noop(), 669 ) 670 if err != nil { 671 t.Fatal(err) 672 } 673 674 select { 675 case readerImpl.connChan <- nil: 676 case <-time.After(time.Second): 677 t.Fatal("Timed out") 678 } 679 680 go func() { 681 for range expMsgs { 682 select { 683 case readerImpl.readChan <- nil: 684 case <-time.After(time.Second): 685 } 686 } 687 }() 688 689 expErrs := []error{} 690 for i := range expMsgs { 691 expErrs = append(expErrs, fmt.Errorf("err %v", i)) 692 } 693 694 resChans := make([]chan<- types.Response, len(expMsgs)) 695 for i, mStr := range expMsgs { 696 var ts types.Transaction 697 var open bool 698 select { 699 case ts, open = <-r.TransactionChan(): 700 if !open { 701 t.Fatal("Chan closed") 702 } 703 if act, exp := string(ts.Payload.Get(0).Get()), mStr; exp != act { 704 t.Errorf("Wrong message returned: %v != %v", act, exp) 705 } 706 resChans[i] = ts.ResponseChan 707 case <-time.After(time.Second): 708 t.Fatal("Timed out") 709 } 710 } 711 712 go func() { 713 for range expErrs { 714 select { 715 case readerImpl.ackChan <- nil: 716 case <-time.After(time.Second): 717 } 718 } 719 }() 720 721 for i, e := range expErrs { 722 select { 723 case resChans[i] <- response.NewError(e): 724 case <-time.After(time.Second): 725 t.Fatal("Timed out") 726 } 727 } 728 729 // We will be failing to send but should still exit immediately. 730 r.CloseAsync() 731 close(readerImpl.readChan) 732 close(readerImpl.connChan) 733 734 if err = r.WaitForClose(time.Second); err != nil { 735 pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 736 t.Fatal(err) 737 } 738 739 if exp, act := expErrs, readerImpl.ackRcvd; !reflect.DeepEqual(exp, act) { 740 t.Errorf("Unexpected errors returned: %v != %v", act, exp) 741 } 742 } 743 744 func TestAsyncReaderSkipAcksAMO(t *testing.T) { 745 exp := [][]byte{[]byte("foo"), []byte("bar")} 746 747 readerImpl := newMockAsyncReader() 748 readerImpl.msgsToSnd = []types.Message{ 749 message.New(exp), 750 message.New(exp), 751 message.New(exp), 752 } 753 754 r, err := NewAsyncReader( 755 "foo", true, readerImpl, 756 log.Noop(), metrics.Noop(), 757 ) 758 if err != nil { 759 t.Error(err) 760 return 761 } 762 763 select { 764 case readerImpl.connChan <- nil: 765 case <-time.After(time.Second): 766 t.Fatal("Timed out") 767 } 768 769 for i := 0; i < 3; i++ { 770 go func() { 771 select { 772 case readerImpl.readChan <- nil: 773 case <-time.After(time.Second): 774 } 775 }() 776 777 var ts types.Transaction 778 var open bool 779 select { 780 case ts, open = <-r.TransactionChan(): 781 if !open { 782 t.Fatal("Chan closed") 783 } 784 if act := message.GetAllBytes(ts.Payload); !reflect.DeepEqual(exp, act) { 785 t.Errorf("Wrong message returned: %s != %s", act, exp) 786 } 787 case <-time.After(time.Second): 788 t.Fatalf("Timed out at attempt: %v", i) 789 } 790 791 select { 792 case ts.ResponseChan <- response.NewUnack(): 793 case <-time.After(time.Second): 794 t.Fatal("Timed out") 795 } 796 } 797 798 // We will be failing to send but should still exit immediately. 799 r.CloseAsync() 800 close(readerImpl.readChan) 801 close(readerImpl.connChan) 802 803 if err = r.WaitForClose(time.Second); err != nil { 804 pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 805 t.Error(err) 806 } 807 808 expErr := "ack not received" 809 if actErr := readerImpl.ackRcvd[0].Error(); expErr != actErr { 810 t.Errorf("Wrong response received: %v != %v", actErr, expErr) 811 } 812 } 813 814 func TestAsyncReaderSkipAcksALO(t *testing.T) { 815 exp := [][]byte{[]byte("foo"), []byte("bar")} 816 817 readerImpl := newMockAsyncReader() 818 readerImpl.msgsToSnd = []types.Message{ 819 message.New(exp), 820 message.New(exp), 821 message.New(exp), 822 } 823 824 r, err := NewAsyncReader( 825 "foo", false, readerImpl, 826 log.Noop(), metrics.Noop(), 827 ) 828 if err != nil { 829 t.Error(err) 830 return 831 } 832 833 select { 834 case readerImpl.connChan <- nil: 835 case <-time.After(time.Second): 836 t.Fatal("Timed out") 837 } 838 839 go func() { 840 select { 841 case readerImpl.readChan <- nil: 842 case <-time.After(time.Second): 843 } 844 }() 845 846 var ts types.Transaction 847 var open bool 848 select { 849 case ts, open = <-r.TransactionChan(): 850 if !open { 851 t.Fatal("Chan closed") 852 } 853 if act := message.GetAllBytes(ts.Payload); !reflect.DeepEqual(exp, act) { 854 t.Errorf("Wrong message returned: %s != %s", act, exp) 855 } 856 case <-time.After(time.Second): 857 t.Fatal("Timed out") 858 } 859 860 select { 861 case ts.ResponseChan <- response.NewUnack(): 862 case <-time.After(time.Second): 863 t.Fatal("Timed out") 864 } 865 866 select { 867 case readerImpl.ackChan <- nil: 868 case <-time.After(time.Second): 869 } 870 871 // Show be closing down. 872 if err = r.WaitForClose(time.Second); err != nil { 873 pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 874 t.Error(err) 875 } 876 877 expErr := "message failed to reach a target destination" 878 if actErr := readerImpl.ackRcvd[0].Error(); expErr != actErr { 879 t.Errorf("Wrong response received: %v != %v", actErr, expErr) 880 } 881 } 882 883 //------------------------------------------------------------------------------ 884 885 func BenchmarkAsyncReaderGenerateN1(b *testing.B) { 886 benchmarkAsyncReaderGenerateN(b, 1) 887 } 888 889 func BenchmarkAsyncReaderGenerateN10(b *testing.B) { 890 benchmarkAsyncReaderGenerateN(b, 10) 891 } 892 893 func BenchmarkAsyncReaderGenerateN100(b *testing.B) { 894 benchmarkAsyncReaderGenerateN(b, 100) 895 } 896 897 func BenchmarkAsyncReaderGenerateN1000(b *testing.B) { 898 benchmarkAsyncReaderGenerateN(b, 1000) 899 } 900 901 func benchmarkAsyncReaderGenerateN(b *testing.B, capacity int) { 902 bloblConf := NewBloblangConfig() 903 bloblConf.Count = 0 904 bloblConf.Interval = "" 905 bloblConf.Mapping = `root = "hello world"` 906 907 readerImpl, err := newBloblang(types.NoopMgr(), bloblConf) 908 require.NoError(b, err) 909 910 r, err := NewAsyncReader("foo", true, readerImpl, log.Noop(), metrics.Noop()) 911 require.NoError(b, err) 912 913 b.Cleanup(func() { 914 r.CloseAsync() 915 if err = r.WaitForClose(time.Second); err != nil { 916 pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 917 b.Fatal(err) 918 } 919 }) 920 921 resChans := make([]chan<- types.Response, capacity) 922 923 b.ReportAllocs() 924 b.ResetTimer() 925 926 for i := 0; i < b.N/capacity; i++ { 927 for j := 0; j < capacity; j++ { 928 select { 929 case ts, open := <-r.TransactionChan(): 930 require.True(b, open) 931 resChans[j] = ts.ResponseChan 932 case <-time.After(time.Second): 933 b.Fatal("Timed out") 934 } 935 } 936 937 for j := 0; j < capacity; j++ { 938 select { 939 case resChans[j] <- response.NewAck(): 940 case <-time.After(time.Second): 941 b.Fatal("Timed out") 942 } 943 } 944 } 945 }