1
0
mirror of https://github.com/redis/go-redis.git synced 2025-12-02 06:22:31 +03:00

Fix broken initialization of idle connections

This commit is contained in:
Nedyalko Dyakov
2025-10-28 19:45:23 +02:00
parent f1c8884250
commit 0752aecdfb
5 changed files with 12 additions and 4 deletions

View File

@@ -699,20 +699,22 @@ func (cn *Conn) GetStateMachine() *ConnStateMachine {
// TryAcquire attempts to acquire the connection for use. // TryAcquire attempts to acquire the connection for use.
// This is an optimized inline method for the hot path (Get operation). // This is an optimized inline method for the hot path (Get operation).
// //
// It tries to transition from IDLE -> IN_USE or CREATED -> IN_USE. // It tries to transition from IDLE -> IN_USE or CREATED -> CREATED.
// Returns true if the connection was successfully acquired, false otherwise. // Returns true if the connection was successfully acquired, false otherwise.
// The CREATED->CREATED is done so we can keep the state correct for later
// initialization of the connection in initConn.
// //
// Performance: This is faster than calling GetStateMachine() + TryTransitionFast() // Performance: This is faster than calling GetStateMachine() + TryTransitionFast()
// //
// NOTE: We directly access cn.stateMachine.state here instead of using the state machine's // NOTE: We directly access cn.stateMachine.state here instead of using the state machine's
// methods. This breaks encapsulation but is necessary for performance. // methods. This breaks encapsulation but is necessary for performance.
// The IDLE->IN_USE and CREATED->IN_USE transitions don't need // The IDLE->IN_USE and CREATED->CREATED transitions don't need
// waiter notification, and benchmarks show 1-3% improvement. If the state machine ever // waiter notification, and benchmarks show 1-3% improvement. If the state machine ever
// needs to notify waiters on these transitions, update this to use TryTransitionFast(). // needs to notify waiters on these transitions, update this to use TryTransitionFast().
func (cn *Conn) TryAcquire() bool { func (cn *Conn) TryAcquire() bool {
// The || operator short-circuits, so only 1 CAS in the common case // The || operator short-circuits, so only 1 CAS in the common case
return cn.stateMachine.state.CompareAndSwap(uint32(StateIdle), uint32(StateInUse)) || return cn.stateMachine.state.CompareAndSwap(uint32(StateIdle), uint32(StateInUse)) ||
cn.stateMachine.state.CompareAndSwap(uint32(StateCreated), uint32(StateInUse)) cn.stateMachine.state.CompareAndSwap(uint32(StateCreated), uint32(StateCreated))
} }
// Release releases the connection back to the pool. // Release releases the connection back to the pool.

View File

@@ -319,6 +319,7 @@ func (cf *ClientFactory) Create(key string, options *CreateClientOptions) (redis
} }
var client redis.UniversalClient var client redis.UniversalClient
var opts interface{}
// Determine if this is a cluster configuration // Determine if this is a cluster configuration
if len(cf.config.Endpoints) > 1 || cf.isClusterEndpoint() { if len(cf.config.Endpoints) > 1 || cf.isClusterEndpoint() {
@@ -349,6 +350,7 @@ func (cf *ClientFactory) Create(key string, options *CreateClientOptions) (redis
} }
} }
opts = clusterOptions
client = redis.NewClusterClient(clusterOptions) client = redis.NewClusterClient(clusterOptions)
} else { } else {
// Create single client // Create single client
@@ -379,9 +381,14 @@ func (cf *ClientFactory) Create(key string, options *CreateClientOptions) (redis
} }
} }
opts = clientOptions
client = redis.NewClient(clientOptions) client = redis.NewClient(clientOptions)
} }
if err := client.Ping(context.Background()).Err(); err != nil {
return nil, fmt.Errorf("failed to connect to Redis: %w\nOptions: %+v", err, opts)
}
// Store the client // Store the client
cf.clients[key] = client cf.clients[key] = client
@@ -832,7 +839,6 @@ func (m *TestDatabaseManager) DeleteDatabase(ctx context.Context) error {
return fmt.Errorf("failed to trigger database deletion: %w", err) return fmt.Errorf("failed to trigger database deletion: %w", err)
} }
// Wait for deletion to complete // Wait for deletion to complete
status, err := m.faultInjector.WaitForAction(ctx, resp.ActionID, status, err := m.faultInjector.WaitForAction(ctx, resp.ActionID,
WithMaxWaitTime(2*time.Minute), WithMaxWaitTime(2*time.Minute),