1
0
mirror of https://github.com/redis/go-redis.git synced 2025-07-18 00:20:57 +03:00

feat: process push notifications before returning connections from pool

Implement push notification processing in baseClient._getConn() to ensure
that all cluster topology changes are handled immediately before connections
are used for commands. This is critical for hitless upgrades and real-time
cluster state awareness.

Key Enhancements:

1. Enhanced Connection Retrieval (_getConn):
   - Process push notifications for both existing and new connections
   - Added processPushNotifications() call before returning connections
   - Ensures immediate handling of cluster topology changes
   - Proper error handling with connection removal on processing failures

2. Push Notification Processing Method:
   - Added processPushNotifications() method to baseClient
   - Only processes notifications for RESP3 connections with processors
   - Uses WithReader() to safely access connection reader
   - Integrates with existing push notification infrastructure

3. Connection Flow Enhancement:
   - Existing connections: Health check → Push notification processing → Return
   - New connections: Initialization → Push notification processing → Return
   - Failed processing results in connection removal and error return
   - Seamless integration with existing connection management

4. RESP3 Protocol Integration:
   - Protocol version check (only process for RESP3)
   - Push processor availability check
   - Graceful handling when processors are not available
   - Consistent behavior with existing push notification system

5. Error Handling and Recovery:
   - Remove connections if push notification processing fails
   - Return errors to trigger connection retry mechanisms
   - Maintain connection pool health and reliability
   - Prevent returning connections with unprocessed notifications

Implementation Details:
- processPushNotifications() checks protocol and processor availability
- Uses cn.WithReader() to safely access the connection reader
- Calls pushProcessor.ProcessPendingNotifications() for actual processing
- Applied to both pooled connections and newly initialized connections
- Consistent error handling across all connection retrieval paths

Flow Enhancement:
1. Connection requested via _getConn()
2. Connection retrieved from pool (existing or new)
3. Connection initialization (if new)
4. Push notification processing (NEW)
5. Connection returned to caller
6. Commands executed with up-to-date cluster state

Benefits:
- Immediate cluster topology awareness before command execution
- Enhanced hitless upgrade reliability with real-time notifications
- Reduced command failures during cluster topology changes
- Consistent push notification handling across all connection types
- Better integration with Redis cluster operations

This ensures that Redis cluster topology changes (MOVING, MIGRATING,
MIGRATED, FAILING_OVER, FAILED_OVER) are always processed before
connections are used, providing the foundation for reliable hitless
upgrades and seamless cluster operations.
This commit is contained in:
Nedyalko Dyakov
2025-07-02 17:04:28 +03:00
parent f4ff2d667c
commit cb8a4e5721

View File

@ -273,6 +273,13 @@ func (c *baseClient) _getConn(ctx context.Context) (*pool.Conn, error) {
} }
if cn.Inited { if cn.Inited {
// Process all pending push notifications before returning the connection
// This ensures that cluster topology changes are handled immediately
if err := c.processPushNotifications(ctx, cn); err != nil {
// If push notification processing fails, remove the connection
c.connPool.Remove(ctx, cn, err)
return nil, err
}
return cn, nil return cn, nil
} }
@ -284,9 +291,32 @@ func (c *baseClient) _getConn(ctx context.Context) (*pool.Conn, error) {
return nil, err return nil, err
} }
// Process any pending push notifications on the newly initialized connection
// This ensures that any notifications received during connection setup are handled
if err := c.processPushNotifications(ctx, cn); err != nil {
// If push notification processing fails, remove the connection
c.connPool.Remove(ctx, cn, err)
return nil, err
}
return cn, nil return cn, nil
} }
// processPushNotifications processes all pending push notifications on a connection
// This ensures that cluster topology changes are handled immediately before the connection is used
func (c *baseClient) processPushNotifications(ctx context.Context, cn *pool.Conn) error {
// Only process push notifications for RESP3 connections with a processor
if c.opt.Protocol != 3 || c.pushProcessor == nil {
return nil
}
// Use WithReader to access the reader and process push notifications
// This is critical for hitless upgrades to work properly
return cn.WithReader(ctx, 0, func(rd *proto.Reader) error {
return c.pushProcessor.ProcessPendingNotifications(ctx, rd)
})
}
func (c *baseClient) newReAuthCredentialsListener(poolCn *pool.Conn) auth.CredentialsListener { func (c *baseClient) newReAuthCredentialsListener(poolCn *pool.Conn) auth.CredentialsListener {
return auth.NewReAuthCredentialsListener( return auth.NewReAuthCredentialsListener(
c.reAuthConnection(poolCn), c.reAuthConnection(poolCn),