|
6 | 6 | "errors"
|
7 | 7 | "fmt"
|
8 | 8 | "net"
|
| 9 | + "sync" |
9 | 10 | "testing"
|
10 | 11 | "time"
|
11 | 12 |
|
@@ -633,3 +634,67 @@ var _ = Describe("Hook with MinIdleConns", func() {
|
633 | 634 | }))
|
634 | 635 | })
|
635 | 636 | })
|
| 637 | + |
| 638 | +var _ = Describe("Dialer connection timeouts", func() { |
| 639 | + var client *redis.Client |
| 640 | + |
| 641 | + const dialSimulatedDelay = 1 * time.Second |
| 642 | + |
| 643 | + BeforeEach(func() { |
| 644 | + options := redisOptions() |
| 645 | + options.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) { |
| 646 | + // Simulated slow dialer. |
| 647 | + // Note that the following sleep is deliberately not context-aware. |
| 648 | + time.Sleep(dialSimulatedDelay) |
| 649 | + return net.Dial("tcp", options.Addr) |
| 650 | + } |
| 651 | + options.MinIdleConns = 1 |
| 652 | + client = redis.NewClient(options) |
| 653 | + }) |
| 654 | + |
| 655 | + AfterEach(func() { |
| 656 | + err := client.Close() |
| 657 | + Expect(err).NotTo(HaveOccurred()) |
| 658 | + }) |
| 659 | + |
| 660 | + It("does not contend on connection dial for concurrent commands", func() { |
| 661 | + var wg sync.WaitGroup |
| 662 | + |
| 663 | + const concurrency = 10 |
| 664 | + |
| 665 | + durations := make(chan time.Duration, concurrency) |
| 666 | + errs := make(chan error, concurrency) |
| 667 | + |
| 668 | + start := time.Now() |
| 669 | + wg.Add(concurrency) |
| 670 | + |
| 671 | + for i := 0; i < concurrency; i++ { |
| 672 | + go func() { |
| 673 | + defer wg.Done() |
| 674 | + |
| 675 | + start := time.Now() |
| 676 | + err := client.Ping(ctx).Err() |
| 677 | + durations <- time.Since(start) |
| 678 | + errs <- err |
| 679 | + }() |
| 680 | + } |
| 681 | + |
| 682 | + wg.Wait() |
| 683 | + close(durations) |
| 684 | + close(errs) |
| 685 | + |
| 686 | + // All commands should eventually succeed, after acquiring a connection. |
| 687 | + for err := range errs { |
| 688 | + Expect(err).NotTo(HaveOccurred()) |
| 689 | + } |
| 690 | + |
| 691 | + // Each individual command should complete within the simulated dial duration bound. |
| 692 | + for duration := range durations { |
| 693 | + Expect(duration).To(BeNumerically("<", 2*dialSimulatedDelay)) |
| 694 | + } |
| 695 | + |
| 696 | + // Due to concurrent execution, the entire test suite should also complete within |
| 697 | + // the same dial duration bound applied for individual commands. |
| 698 | + Expect(time.Since(start)).To(BeNumerically("<", 2*dialSimulatedDelay)) |
| 699 | + }) |
| 700 | +}) |
0 commit comments