mirror of
https://github.com/redis/go-redis.git
synced 2025-12-02 06:22:31 +03:00
[maintnotif] Cluster specific handlers (#3613)
* maint notification handlers for cluster messages * unrelax all conns * trigger ci on feature branches
This commit is contained in:
@@ -49,6 +49,10 @@ func (snh *NotificationHandler) HandlePushNotification(ctx context.Context, hand
|
||||
err = snh.handleFailingOver(ctx, handlerCtx, modifiedNotification)
|
||||
case NotificationFailedOver:
|
||||
err = snh.handleFailedOver(ctx, handlerCtx, modifiedNotification)
|
||||
case NotificationSMigrating:
|
||||
err = snh.handleSMigrating(ctx, handlerCtx, modifiedNotification)
|
||||
case NotificationSMigrated:
|
||||
err = snh.handleSMigrated(ctx, handlerCtx, modifiedNotification)
|
||||
default:
|
||||
// Ignore other notification types (e.g., pub/sub messages)
|
||||
err = nil
|
||||
@@ -61,7 +65,9 @@ func (snh *NotificationHandler) HandlePushNotification(ctx context.Context, hand
|
||||
}
|
||||
|
||||
// handleMoving processes MOVING notifications.
|
||||
// ["MOVING", seqNum, timeS, endpoint] - per-connection handoff
|
||||
// MOVING indicates that a connection should be handed off to a new endpoint.
|
||||
// This is a per-connection notification that triggers connection handoff.
|
||||
// Expected format: ["MOVING", seqNum, timeS, endpoint]
|
||||
func (snh *NotificationHandler) handleMoving(ctx context.Context, handlerCtx push.NotificationHandlerContext, notification []interface{}) error {
|
||||
if len(notification) < 3 {
|
||||
internal.Logger.Printf(ctx, logs.InvalidNotification("MOVING", notification))
|
||||
@@ -167,9 +173,10 @@ func (snh *NotificationHandler) markConnForHandoff(conn *pool.Conn, newEndpoint
|
||||
}
|
||||
|
||||
// handleMigrating processes MIGRATING notifications.
|
||||
// MIGRATING indicates that a connection migration is starting.
|
||||
// This is a per-connection notification that applies relaxed timeouts.
|
||||
// Expected format: ["MIGRATING", ...]
|
||||
func (snh *NotificationHandler) handleMigrating(ctx context.Context, handlerCtx push.NotificationHandlerContext, notification []interface{}) error {
|
||||
// MIGRATING notifications indicate that a connection is about to be migrated
|
||||
// Apply relaxed timeouts to the specific connection that received this notification
|
||||
if len(notification) < 2 {
|
||||
internal.Logger.Printf(ctx, logs.InvalidNotification("MIGRATING", notification))
|
||||
return ErrInvalidNotification
|
||||
@@ -195,9 +202,10 @@ func (snh *NotificationHandler) handleMigrating(ctx context.Context, handlerCtx
|
||||
}
|
||||
|
||||
// handleMigrated processes MIGRATED notifications.
|
||||
// MIGRATED indicates that a connection migration has completed.
|
||||
// This is a per-connection notification that clears relaxed timeouts.
|
||||
// Expected format: ["MIGRATED", ...]
|
||||
func (snh *NotificationHandler) handleMigrated(ctx context.Context, handlerCtx push.NotificationHandlerContext, notification []interface{}) error {
|
||||
// MIGRATED notifications indicate that a connection migration has completed
|
||||
// Restore normal timeouts for the specific connection that received this notification
|
||||
if len(notification) < 2 {
|
||||
internal.Logger.Printf(ctx, logs.InvalidNotification("MIGRATED", notification))
|
||||
return ErrInvalidNotification
|
||||
@@ -224,9 +232,10 @@ func (snh *NotificationHandler) handleMigrated(ctx context.Context, handlerCtx p
|
||||
}
|
||||
|
||||
// handleFailingOver processes FAILING_OVER notifications.
|
||||
// FAILING_OVER indicates that a failover is starting.
|
||||
// This is a per-connection notification that applies relaxed timeouts.
|
||||
// Expected format: ["FAILING_OVER", ...]
|
||||
func (snh *NotificationHandler) handleFailingOver(ctx context.Context, handlerCtx push.NotificationHandlerContext, notification []interface{}) error {
|
||||
// FAILING_OVER notifications indicate that a connection is about to failover
|
||||
// Apply relaxed timeouts to the specific connection that received this notification
|
||||
if len(notification) < 2 {
|
||||
internal.Logger.Printf(ctx, logs.InvalidNotification("FAILING_OVER", notification))
|
||||
return ErrInvalidNotification
|
||||
@@ -253,9 +262,10 @@ func (snh *NotificationHandler) handleFailingOver(ctx context.Context, handlerCt
|
||||
}
|
||||
|
||||
// handleFailedOver processes FAILED_OVER notifications.
|
||||
// FAILED_OVER indicates that a failover has completed.
|
||||
// This is a per-connection notification that clears relaxed timeouts.
|
||||
// Expected format: ["FAILED_OVER", ...]
|
||||
func (snh *NotificationHandler) handleFailedOver(ctx context.Context, handlerCtx push.NotificationHandlerContext, notification []interface{}) error {
|
||||
// FAILED_OVER notifications indicate that a connection failover has completed
|
||||
// Restore normal timeouts for the specific connection that received this notification
|
||||
if len(notification) < 2 {
|
||||
internal.Logger.Printf(ctx, logs.InvalidNotification("FAILED_OVER", notification))
|
||||
return ErrInvalidNotification
|
||||
@@ -280,3 +290,107 @@ func (snh *NotificationHandler) handleFailedOver(ctx context.Context, handlerCtx
|
||||
conn.ClearRelaxedTimeout()
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleSMigrating processes SMIGRATING notifications.
|
||||
// SMIGRATING indicates that a cluster slot is in the process of migrating to a different node.
|
||||
// This is a per-connection notification that applies relaxed timeouts during slot migration.
|
||||
// Expected format: ["SMIGRATING", SeqID, slot/range1-range2, ...]
|
||||
func (snh *NotificationHandler) handleSMigrating(ctx context.Context, handlerCtx push.NotificationHandlerContext, notification []interface{}) error {
|
||||
if len(notification) < 3 {
|
||||
internal.Logger.Printf(ctx, logs.InvalidNotification("SMIGRATING", notification))
|
||||
return ErrInvalidNotification
|
||||
}
|
||||
|
||||
// Extract SeqID (position 1)
|
||||
seqID, ok := notification[1].(int64)
|
||||
if !ok {
|
||||
internal.Logger.Printf(ctx, logs.InvalidSeqIDInSMigratingNotification(notification[1]))
|
||||
return ErrInvalidNotification
|
||||
}
|
||||
|
||||
// Extract slot ranges (position 2+)
|
||||
// For now, we just extract them for logging
|
||||
// Format can be: single slot "1234" or range "100-200"
|
||||
var slotRanges []string
|
||||
for i := 2; i < len(notification); i++ {
|
||||
if slotRange, ok := notification[i].(string); ok {
|
||||
slotRanges = append(slotRanges, slotRange)
|
||||
}
|
||||
}
|
||||
|
||||
if handlerCtx.Conn == nil {
|
||||
internal.Logger.Printf(ctx, logs.NoConnectionInHandlerContext("SMIGRATING"))
|
||||
return ErrInvalidNotification
|
||||
}
|
||||
|
||||
conn, ok := handlerCtx.Conn.(*pool.Conn)
|
||||
if !ok {
|
||||
internal.Logger.Printf(ctx, logs.InvalidConnectionTypeInHandlerContext("SMIGRATING", handlerCtx.Conn, handlerCtx))
|
||||
return ErrInvalidNotification
|
||||
}
|
||||
|
||||
// Apply relaxed timeout to this specific connection
|
||||
if internal.LogLevel.InfoOrAbove() {
|
||||
internal.Logger.Printf(ctx, logs.SlotMigrating(conn.GetID(), seqID, slotRanges))
|
||||
}
|
||||
conn.SetRelaxedTimeout(snh.manager.config.RelaxedTimeout, snh.manager.config.RelaxedTimeout)
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleSMigrated processes SMIGRATED notifications.
|
||||
// SMIGRATED indicates that a cluster slot has finished migrating to a different node.
|
||||
// This is a cluster-level notification that triggers cluster state reload.
|
||||
// Expected format: ["SMIGRATED", SeqID, host:port, slot1/range1-range2, ...]
|
||||
// Note: Multiple connections may receive the same notification, so we deduplicate by SeqID before triggering reload.
|
||||
// but we still process the notification on each connection to clear the relaxed timeout.
|
||||
func (snh *NotificationHandler) handleSMigrated(ctx context.Context, handlerCtx push.NotificationHandlerContext, notification []interface{}) error {
|
||||
if len(notification) < 4 {
|
||||
internal.Logger.Printf(ctx, logs.InvalidNotification("SMIGRATED", notification))
|
||||
return ErrInvalidNotification
|
||||
}
|
||||
|
||||
// Extract SeqID (position 1)
|
||||
seqID, ok := notification[1].(int64)
|
||||
if !ok {
|
||||
internal.Logger.Printf(ctx, logs.InvalidSeqIDInSMigratedNotification(notification[1]))
|
||||
return ErrInvalidNotification
|
||||
}
|
||||
|
||||
// Deduplicate by SeqID - multiple connections may receive the same notification
|
||||
if snh.manager.MarkSMigratedSeqIDProcessed(seqID) {
|
||||
// Extract host:port (position 2)
|
||||
hostPort, ok := notification[2].(string)
|
||||
if !ok {
|
||||
internal.Logger.Printf(ctx, logs.InvalidHostPortInSMigratedNotification(notification[2]))
|
||||
return ErrInvalidNotification
|
||||
}
|
||||
|
||||
// Extract slot ranges (position 3+)
|
||||
// For now, we just extract them for logging
|
||||
// Format can be: single slot "1234" or range "100-200"
|
||||
var slotRanges []string
|
||||
for i := 3; i < len(notification); i++ {
|
||||
if slotRange, ok := notification[i].(string); ok {
|
||||
slotRanges = append(slotRanges, slotRange)
|
||||
}
|
||||
}
|
||||
|
||||
if internal.LogLevel.InfoOrAbove() {
|
||||
internal.Logger.Printf(ctx, logs.SlotMigrated(seqID, hostPort, slotRanges))
|
||||
}
|
||||
// Trigger cluster state reload via callback, passing host:port and slot ranges
|
||||
// For now, implementations just log these and trigger a full reload
|
||||
// In the future, this could be optimized to reload only the specific slots
|
||||
snh.manager.TriggerClusterStateReload(ctx, hostPort, slotRanges)
|
||||
}
|
||||
|
||||
// clear relaxed timeout
|
||||
if handlerCtx.Conn != nil {
|
||||
conn, ok := handlerCtx.Conn.(*pool.Conn)
|
||||
if ok {
|
||||
conn.ClearRelaxedTimeout()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user