mirror of
https://github.com/redis/go-redis.git
synced 2025-12-02 06:22:31 +03:00
initConn sets IDLE state
- Handle unexpected conn state changes
This commit is contained in:
@@ -603,6 +603,7 @@ func (cn *Conn) SetNetConnAndInitConn(ctx context.Context, netConn net.Conn) err
|
|||||||
// Execute initialization
|
// Execute initialization
|
||||||
// NOTE: ExecuteInitConn (via baseClient.initConn) will transition to IDLE on success
|
// NOTE: ExecuteInitConn (via baseClient.initConn) will transition to IDLE on success
|
||||||
// or CLOSED on failure. We don't need to do it here.
|
// or CLOSED on failure. We don't need to do it here.
|
||||||
|
// NOTE: Initconn returns conn in IDLE state
|
||||||
initErr := cn.ExecuteInitConn(ctx)
|
initErr := cn.ExecuteInitConn(ctx)
|
||||||
if initErr != nil {
|
if initErr != nil {
|
||||||
// ExecuteInitConn already transitioned to CLOSED, just return the error
|
// ExecuteInitConn already transitioned to CLOSED, just return the error
|
||||||
@@ -745,6 +746,7 @@ func (cn *Conn) ClearHandoffState() {
|
|||||||
|
|
||||||
// Mark connection as usable again
|
// Mark connection as usable again
|
||||||
// Use state machine directly instead of deprecated SetUsable
|
// Use state machine directly instead of deprecated SetUsable
|
||||||
|
// probably done by initConn
|
||||||
cn.stateMachine.Transition(StateIdle)
|
cn.stateMachine.Transition(StateIdle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -684,11 +684,22 @@ func (p *ConnPool) putConn(ctx context.Context, cn *Conn, freeTurn bool) {
|
|||||||
// Using inline Release() method for better performance (avoids pointer dereference)
|
// Using inline Release() method for better performance (avoids pointer dereference)
|
||||||
transitionedToIdle := cn.Release()
|
transitionedToIdle := cn.Release()
|
||||||
|
|
||||||
|
// Handle unexpected state changes
|
||||||
if !transitionedToIdle {
|
if !transitionedToIdle {
|
||||||
// Fast path failed - hook might have changed state (e.g., to UNUSABLE for handoff)
|
// Fast path failed - hook might have changed state (e.g., to UNUSABLE for handoff)
|
||||||
// Keep the state set by the hook and pool the connection anyway
|
// Keep the state set by the hook and pool the connection anyway
|
||||||
currentState := cn.GetStateMachine().GetState()
|
currentState := cn.GetStateMachine().GetState()
|
||||||
internal.Logger.Printf(ctx, "Connection state changed by hook to %v, pooling as-is", currentState)
|
switch currentState {
|
||||||
|
case StateUnusable:
|
||||||
|
// expected state, don't log it
|
||||||
|
case StateClosed:
|
||||||
|
internal.Logger.Printf(ctx, "Unexpected conn[%d] state changed by hook to %v, closing it", cn.GetID(), currentState)
|
||||||
|
shouldCloseConn = true
|
||||||
|
p.removeConnWithLock(cn)
|
||||||
|
default:
|
||||||
|
// Pool as-is
|
||||||
|
internal.Logger.Printf(ctx, "Unexpected conn[%d] state changed by hook to %v, pooling as-is", cn.GetID(), currentState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unusable conns are expected to become usable at some point (background process is reconnecting them)
|
// unusable conns are expected to become usable at some point (background process is reconnecting them)
|
||||||
@@ -704,15 +715,16 @@ func (p *ConnPool) putConn(ctx context.Context, cn *Conn, freeTurn bool) {
|
|||||||
p.idleConns = append([]*Conn{cn}, p.idleConns...)
|
p.idleConns = append([]*Conn{cn}, p.idleConns...)
|
||||||
p.connsMu.Unlock()
|
p.connsMu.Unlock()
|
||||||
}
|
}
|
||||||
} else {
|
p.idleConnsLen.Add(1)
|
||||||
|
} else if !shouldCloseConn {
|
||||||
p.connsMu.Lock()
|
p.connsMu.Lock()
|
||||||
p.idleConns = append(p.idleConns, cn)
|
p.idleConns = append(p.idleConns, cn)
|
||||||
p.connsMu.Unlock()
|
p.connsMu.Unlock()
|
||||||
|
p.idleConnsLen.Add(1)
|
||||||
}
|
}
|
||||||
p.idleConnsLen.Add(1)
|
|
||||||
} else {
|
} else {
|
||||||
p.removeConnWithLock(cn)
|
|
||||||
shouldCloseConn = true
|
shouldCloseConn = true
|
||||||
|
p.removeConnWithLock(cn)
|
||||||
}
|
}
|
||||||
|
|
||||||
if freeTurn {
|
if freeTurn {
|
||||||
|
|||||||
@@ -456,6 +456,8 @@ func (hwm *handoffWorkerManager) performHandoffInternal(
|
|||||||
// - set the connection as usable again
|
// - set the connection as usable again
|
||||||
// - clear the handoff state (shouldHandoff, endpoint, seqID)
|
// - clear the handoff state (shouldHandoff, endpoint, seqID)
|
||||||
// - reset the handoff retries to 0
|
// - reset the handoff retries to 0
|
||||||
|
// Note: Theoretically there may be a short window where the connection is in the pool
|
||||||
|
// and IDLE (initConn completed) but still has handoff state set.
|
||||||
conn.ClearHandoffState()
|
conn.ClearHandoffState()
|
||||||
internal.Logger.Printf(ctx, logs.HandoffSucceeded(connID, newEndpoint))
|
internal.Logger.Printf(ctx, logs.HandoffSucceeded(connID, newEndpoint))
|
||||||
|
|
||||||
|
|||||||
6
redis.go
6
redis.go
@@ -298,6 +298,12 @@ func (c *baseClient) _getConn(ctx context.Context) (*pool.Conn, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initConn will transition to IDLE state, so we need to acquire it
|
||||||
|
// before returning it to the user.
|
||||||
|
if !cn.TryAcquire() {
|
||||||
|
return nil, fmt.Errorf("redis: connection is not usable")
|
||||||
|
}
|
||||||
|
|
||||||
return cn, nil
|
return cn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user