|
1 | 1 | package sarama
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "fmt" |
4 | 5 | "io"
|
5 | 6 | "sync"
|
6 | 7 | "sync/atomic"
|
@@ -612,6 +613,75 @@ func TestClientController(t *testing.T) {
|
612 | 613 | }
|
613 | 614 | }
|
614 | 615 |
|
| 616 | +func TestClientMetadataTimeout(t *testing.T) { |
| 617 | + for _, timeout := range []time.Duration{ |
| 618 | + 250 * time.Millisecond, // Will cut the first retry pass |
| 619 | + 500 * time.Millisecond, // Will cut the second retry pass |
| 620 | + 750 * time.Millisecond, // Will cut the third retry pass |
| 621 | + 900 * time.Millisecond, // Will stop after the three retries |
| 622 | + } { |
| 623 | + t.Run(fmt.Sprintf("timeout=%v", timeout), func(t *testing.T) { |
| 624 | + // Use a responsive broker to create a working client |
| 625 | + initialSeed := NewMockBroker(t, 0) |
| 626 | + emptyMetadata := new(MetadataResponse) |
| 627 | + initialSeed.Returns(emptyMetadata) |
| 628 | + |
| 629 | + conf := NewConfig() |
| 630 | + // Speed up the metadata request failure because of a read timeout |
| 631 | + conf.Net.ReadTimeout = 100 * time.Millisecond |
| 632 | + // Disable backoff and refresh |
| 633 | + conf.Metadata.Retry.Backoff = 0 |
| 634 | + conf.Metadata.RefreshFrequency = 0 |
| 635 | + // But configure a "global" timeout |
| 636 | + conf.Metadata.Timeout = timeout |
| 637 | + c, err := NewClient([]string{initialSeed.Addr()}, conf) |
| 638 | + if err != nil { |
| 639 | + t.Fatal(err) |
| 640 | + } |
| 641 | + initialSeed.Close() |
| 642 | + |
| 643 | + client := c.(*client) |
| 644 | + |
| 645 | + // Start seed brokers that do not reply to anything and therefore a read |
| 646 | + // on the TCP connection will timeout to simulate unresponsive brokers |
| 647 | + seed1 := NewMockBroker(t, 1) |
| 648 | + defer seed1.Close() |
| 649 | + seed2 := NewMockBroker(t, 2) |
| 650 | + defer seed2.Close() |
| 651 | + |
| 652 | + // Overwrite the seed brokers with a fixed ordering to make this test deterministic |
| 653 | + safeClose(t, client.seedBrokers[0]) |
| 654 | + client.seedBrokers = []*Broker{NewBroker(seed1.Addr()), NewBroker(seed2.Addr())} |
| 655 | + client.deadSeeds = []*Broker{} |
| 656 | + |
| 657 | + // Start refreshing metadata in the background |
| 658 | + errChan := make(chan error) |
| 659 | + start := time.Now() |
| 660 | + go func() { |
| 661 | + errChan <- c.RefreshMetadata() |
| 662 | + }() |
| 663 | + |
| 664 | + // Check that the refresh fails fast enough (less than twice the configured timeout) |
| 665 | + // instead of at least: 100 ms * 2 brokers * 3 retries = 800 ms |
| 666 | + maxRefreshDuration := 2 * timeout |
| 667 | + select { |
| 668 | + case err := <-errChan: |
| 669 | + t.Logf("Got err: %v after waiting for: %v", err, time.Since(start)) |
| 670 | + if err == nil { |
| 671 | + t.Fatal("Expected failed RefreshMetadata, got nil") |
| 672 | + } |
| 673 | + if err != ErrOutOfBrokers { |
| 674 | + t.Error("Expected failed RefreshMetadata with ErrOutOfBrokers, got:", err) |
| 675 | + } |
| 676 | + case <-time.After(maxRefreshDuration): |
| 677 | + t.Fatalf("RefreshMetadata did not fail fast enough after waiting for %v", maxRefreshDuration) |
| 678 | + } |
| 679 | + |
| 680 | + safeClose(t, c) |
| 681 | + }) |
| 682 | + } |
| 683 | +} |
| 684 | + |
615 | 685 | func TestClientCoordinatorWithConsumerOffsetsTopic(t *testing.T) {
|
616 | 686 | seedBroker := NewMockBroker(t, 1)
|
617 | 687 | staleCoordinator := NewMockBroker(t, 2)
|
|
0 commit comments