Skip to content

Commit b42c19b

Browse files
committed
add additional options for dialer retries
1 parent e6d4b46 commit b42c19b

File tree

4 files changed

+103
-2
lines changed

4 files changed

+103
-2
lines changed

hitless/example_hooks.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ type MetricsHook struct {
2121
NotificationCounts map[string]int64
2222
ProcessingTimes map[string]time.Duration
2323
ErrorCounts map[string]int64
24+
HandoffCounts int64 // Total handoffs initiated
25+
HandoffSuccesses int64 // Successful handoffs
26+
HandoffFailures int64 // Failed handoffs
2427
}
2528

2629
// NewMetricsHook creates a new metrics collection hook.

internal/pool/pool.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ type Options struct {
9999
ConnMaxIdleTime time.Duration
100100
ConnMaxLifetime time.Duration
101101
PushNotificationsEnabled bool
102+
103+
// DialerRetries is the maximum number of retry attempts when dialing fails.
104+
// Default: 5
105+
DialerRetries int
106+
107+
// DialerRetryTimeout is the backoff duration between retry attempts.
108+
// Default: 100ms
109+
DialerRetryTimeout time.Duration
102110
}
103111

104112
type lastDialErrorWrap struct {
@@ -315,8 +323,14 @@ func (p *ConnPool) dialConn(ctx context.Context, pooled bool) (*Conn, error) {
315323
// Retry dialing with backoff
316324
// the context timeout is already handled by the context passed in
317325
// so we may never reach the max retries, higher values don't hurt
318-
const maxRetries = 10
319-
const backoffDuration = 100 * time.Millisecond
326+
maxRetries := p.cfg.DialerRetries
327+
if maxRetries <= 0 {
328+
maxRetries = 5 // Default value
329+
}
330+
backoffDuration := p.cfg.DialerRetryTimeout
331+
if backoffDuration <= 0 {
332+
backoffDuration = 100 * time.Millisecond // Default value
333+
}
320334

321335
var lastErr error
322336
shouldLoop := true

internal/pool/pool_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,70 @@ var _ = Describe("race", func() {
437437
})
438438
})
439439

440+
// TestDialerRetryConfiguration tests the new DialerRetries and DialerRetryTimeout options
441+
func TestDialerRetryConfiguration(t *testing.T) {
442+
ctx := context.Background()
443+
444+
t.Run("CustomDialerRetries", func(t *testing.T) {
445+
attempts := 0
446+
failingDialer := func(ctx context.Context) (net.Conn, error) {
447+
attempts++
448+
return nil, errors.New("dial failed")
449+
}
450+
451+
connPool := pool.NewConnPool(&pool.Options{
452+
Dialer: failingDialer,
453+
PoolSize: 1,
454+
PoolTimeout: time.Second,
455+
DialTimeout: time.Second,
456+
DialerRetries: 3, // Custom retry count
457+
DialerRetryTimeout: 10 * time.Millisecond, // Fast retries for testing
458+
})
459+
defer connPool.Close()
460+
461+
_, err := connPool.Get(ctx)
462+
if err == nil {
463+
t.Error("Expected error from failing dialer")
464+
}
465+
466+
// Should have attempted at least 3 times (DialerRetries = 3)
467+
// There might be additional attempts due to pool logic
468+
if attempts < 3 {
469+
t.Errorf("Expected at least 3 dial attempts, got %d", attempts)
470+
}
471+
if attempts > 6 {
472+
t.Errorf("Expected around 3 dial attempts, got %d (too many)", attempts)
473+
}
474+
})
475+
476+
t.Run("DefaultDialerRetries", func(t *testing.T) {
477+
attempts := 0
478+
failingDialer := func(ctx context.Context) (net.Conn, error) {
479+
attempts++
480+
return nil, errors.New("dial failed")
481+
}
482+
483+
connPool := pool.NewConnPool(&pool.Options{
484+
Dialer: failingDialer,
485+
PoolSize: 1,
486+
PoolTimeout: time.Second,
487+
DialTimeout: time.Second,
488+
// DialerRetries and DialerRetryTimeout not set - should use defaults
489+
})
490+
defer connPool.Close()
491+
492+
_, err := connPool.Get(ctx)
493+
if err == nil {
494+
t.Error("Expected error from failing dialer")
495+
}
496+
497+
// Should have attempted 5 times (default DialerRetries = 5)
498+
if attempts != 5 {
499+
t.Errorf("Expected 5 dial attempts (default), got %d", attempts)
500+
}
501+
})
502+
}
503+
440504
func init() {
441505
logging.Disable()
442506
}

options.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ type Options struct {
112112
// default: 10 seconds
113113
DialTimeout time.Duration
114114

115+
// DialerRetries is the maximum number of retry attempts when dialing fails.
116+
//
117+
// default: 5
118+
DialerRetries int
119+
120+
// DialerRetryTimeout is the backoff duration between retry attempts.
121+
//
122+
// default: 100 milliseconds
123+
DialerRetryTimeout time.Duration
124+
115125
// ReadTimeout for socket reads. If reached, commands will fail
116126
// with a timeout instead of blocking. Supported values:
117127
//
@@ -277,6 +287,12 @@ func (opt *Options) init() {
277287
if opt.DialTimeout == 0 {
278288
opt.DialTimeout = 10 * time.Second
279289
}
290+
if opt.DialerRetries == 0 {
291+
opt.DialerRetries = 5
292+
}
293+
if opt.DialerRetryTimeout == 0 {
294+
opt.DialerRetryTimeout = 100 * time.Millisecond
295+
}
280296
if opt.Dialer == nil {
281297
opt.Dialer = NewDialer(opt)
282298
}
@@ -678,6 +694,8 @@ func newConnPool(
678694
PoolSize: poolSize,
679695
PoolTimeout: opt.PoolTimeout,
680696
DialTimeout: opt.DialTimeout,
697+
DialerRetries: opt.DialerRetries,
698+
DialerRetryTimeout: opt.DialerRetryTimeout,
681699
MinIdleConns: minIdleConns,
682700
MaxIdleConns: maxIdleConns,
683701
MaxActiveConns: maxActiveConns,
@@ -716,6 +734,8 @@ func newPubSubPool(opt *Options, dialer func(ctx context.Context, network, addr
716734
PoolSize: poolSize,
717735
PoolTimeout: opt.PoolTimeout,
718736
DialTimeout: opt.DialTimeout,
737+
DialerRetries: opt.DialerRetries,
738+
DialerRetryTimeout: opt.DialerRetryTimeout,
719739
MinIdleConns: minIdleConns,
720740
MaxIdleConns: maxIdleConns,
721741
MaxActiveConns: maxActiveConns,

0 commit comments

Comments
 (0)