From 94fa9204ce1ae18c7a2b774585033057936fe552 Mon Sep 17 00:00:00 2001 From: Nedyalko Dyakov Date: Fri, 24 Oct 2025 15:28:19 +0300 Subject: [PATCH] better timeouts --- internal/pool/conn.go | 12 ++++++++---- redis.go | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/internal/pool/conn.go b/internal/pool/conn.go index a0ec6580..72664b4d 100644 --- a/internal/pool/conn.go +++ b/internal/pool/conn.go @@ -69,7 +69,7 @@ type Conn struct { // Handoff metadata - managed separately from state machine // These are atomic for lock-free access during handoff operations - handoffStateAtomic atomic.Value // stores *HandoffState + handoffStateAtomic atomic.Value // stores *HandoffState handoffRetriesAtomic atomic.Uint32 // retry counter pooled bool @@ -536,10 +536,14 @@ func (cn *Conn) SetNetConnAndInitConn(ctx context.Context, netConn net.Conn) err // Wait for and transition to INITIALIZING state - this prevents concurrent initializations // Valid from states: CREATED (first init), IDLE (reconnect), UNUSABLE (handoff/reauth) // If another goroutine is initializing, we'll wait for it to finish - // Add 1ms timeout to prevent indefinite blocking - waitCtx, cancel := context.WithTimeout(ctx, time.Millisecond) + // if the context has a deadline, use that, otherwise use the connection read (relaxed) timeout + // which should be set during handoff. If it is not set, use a 5 second default + deadline, ok := ctx.Deadline() + if !ok { + deadline = time.Now().Add(cn.getEffectiveReadTimeout(5 * time.Second)) + } + waitCtx, cancel := context.WithDeadline(ctx, deadline) defer cancel() - finalState, err := cn.stateMachine.AwaitAndTransition( waitCtx, []ConnState{StateCreated, StateIdle, StateUnusable}, diff --git a/redis.go b/redis.go index 9a1c8773..a355531c 100644 --- a/redis.go +++ b/redis.go @@ -394,8 +394,8 @@ func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error { if finalState == pool.StateInitializing { // Another goroutine is initializing - WAIT for it to complete // Use AwaitAndTransition to wait for IDLE or IN_USE state - // Add 1ms timeout to prevent indefinite blocking - waitCtx, cancel := context.WithTimeout(ctx, time.Millisecond) + // use DialTimeout as the timeout for the wait + waitCtx, cancel := context.WithTimeout(ctx, c.opt.DialTimeout) defer cancel() finalState, err := cn.GetStateMachine().AwaitAndTransition(