@@ -49,6 +49,10 @@ func (snh *NotificationHandler) HandlePushNotification(ctx context.Context, hand
4949 err = snh .handleFailingOver (ctx , handlerCtx , modifiedNotification )
5050 case NotificationFailedOver :
5151 err = snh .handleFailedOver (ctx , handlerCtx , modifiedNotification )
52+ case NotificationSMigrating :
53+ err = snh .handleSMigrating (ctx , handlerCtx , modifiedNotification )
54+ case NotificationSMigrated :
55+ err = snh .handleSMigrated (ctx , handlerCtx , modifiedNotification )
5256 default :
5357 // Ignore other notification types (e.g., pub/sub messages)
5458 err = nil
@@ -61,7 +65,9 @@ func (snh *NotificationHandler) HandlePushNotification(ctx context.Context, hand
6165}
6266
6367// handleMoving processes MOVING notifications.
64- // ["MOVING", seqNum, timeS, endpoint] - per-connection handoff
68+ // MOVING indicates that a connection should be handed off to a new endpoint.
69+ // This is a per-connection notification that triggers connection handoff.
70+ // Expected format: ["MOVING", seqNum, timeS, endpoint]
6571func (snh * NotificationHandler ) handleMoving (ctx context.Context , handlerCtx push.NotificationHandlerContext , notification []interface {}) error {
6672 if len (notification ) < 3 {
6773 internal .Logger .Printf (ctx , logs .InvalidNotification ("MOVING" , notification ))
@@ -167,9 +173,10 @@ func (snh *NotificationHandler) markConnForHandoff(conn *pool.Conn, newEndpoint
167173}
168174
169175// handleMigrating processes MIGRATING notifications.
176+ // MIGRATING indicates that a connection migration is starting.
177+ // This is a per-connection notification that applies relaxed timeouts.
178+ // Expected format: ["MIGRATING", ...]
170179func (snh * NotificationHandler ) handleMigrating (ctx context.Context , handlerCtx push.NotificationHandlerContext , notification []interface {}) error {
171- // MIGRATING notifications indicate that a connection is about to be migrated
172- // Apply relaxed timeouts to the specific connection that received this notification
173180 if len (notification ) < 2 {
174181 internal .Logger .Printf (ctx , logs .InvalidNotification ("MIGRATING" , notification ))
175182 return ErrInvalidNotification
@@ -195,9 +202,10 @@ func (snh *NotificationHandler) handleMigrating(ctx context.Context, handlerCtx
195202}
196203
197204// handleMigrated processes MIGRATED notifications.
205+ // MIGRATED indicates that a connection migration has completed.
206+ // This is a per-connection notification that clears relaxed timeouts.
207+ // Expected format: ["MIGRATED", ...]
198208func (snh * NotificationHandler ) handleMigrated (ctx context.Context , handlerCtx push.NotificationHandlerContext , notification []interface {}) error {
199- // MIGRATED notifications indicate that a connection migration has completed
200- // Restore normal timeouts for the specific connection that received this notification
201209 if len (notification ) < 2 {
202210 internal .Logger .Printf (ctx , logs .InvalidNotification ("MIGRATED" , notification ))
203211 return ErrInvalidNotification
@@ -224,9 +232,10 @@ func (snh *NotificationHandler) handleMigrated(ctx context.Context, handlerCtx p
224232}
225233
226234// handleFailingOver processes FAILING_OVER notifications.
235+ // FAILING_OVER indicates that a failover is starting.
236+ // This is a per-connection notification that applies relaxed timeouts.
237+ // Expected format: ["FAILING_OVER", ...]
227238func (snh * NotificationHandler ) handleFailingOver (ctx context.Context , handlerCtx push.NotificationHandlerContext , notification []interface {}) error {
228- // FAILING_OVER notifications indicate that a connection is about to failover
229- // Apply relaxed timeouts to the specific connection that received this notification
230239 if len (notification ) < 2 {
231240 internal .Logger .Printf (ctx , logs .InvalidNotification ("FAILING_OVER" , notification ))
232241 return ErrInvalidNotification
@@ -253,9 +262,10 @@ func (snh *NotificationHandler) handleFailingOver(ctx context.Context, handlerCt
253262}
254263
255264// handleFailedOver processes FAILED_OVER notifications.
265+ // FAILED_OVER indicates that a failover has completed.
266+ // This is a per-connection notification that clears relaxed timeouts.
267+ // Expected format: ["FAILED_OVER", ...]
256268func (snh * NotificationHandler ) handleFailedOver (ctx context.Context , handlerCtx push.NotificationHandlerContext , notification []interface {}) error {
257- // FAILED_OVER notifications indicate that a connection failover has completed
258- // Restore normal timeouts for the specific connection that received this notification
259269 if len (notification ) < 2 {
260270 internal .Logger .Printf (ctx , logs .InvalidNotification ("FAILED_OVER" , notification ))
261271 return ErrInvalidNotification
@@ -280,3 +290,107 @@ func (snh *NotificationHandler) handleFailedOver(ctx context.Context, handlerCtx
280290 conn .ClearRelaxedTimeout ()
281291 return nil
282292}
293+
294+ // handleSMigrating processes SMIGRATING notifications.
295+ // SMIGRATING indicates that a cluster slot is in the process of migrating to a different node.
296+ // This is a per-connection notification that applies relaxed timeouts during slot migration.
297+ // Expected format: ["SMIGRATING", SeqID, slot/range1-range2, ...]
298+ func (snh * NotificationHandler ) handleSMigrating (ctx context.Context , handlerCtx push.NotificationHandlerContext , notification []interface {}) error {
299+ if len (notification ) < 3 {
300+ internal .Logger .Printf (ctx , logs .InvalidNotification ("SMIGRATING" , notification ))
301+ return ErrInvalidNotification
302+ }
303+
304+ // Extract SeqID (position 1)
305+ seqID , ok := notification [1 ].(int64 )
306+ if ! ok {
307+ internal .Logger .Printf (ctx , logs .InvalidSeqIDInSMigratingNotification (notification [1 ]))
308+ return ErrInvalidNotification
309+ }
310+
311+ // Extract slot ranges (position 2+)
312+ // For now, we just extract them for logging
313+ // Format can be: single slot "1234" or range "100-200"
314+ var slotRanges []string
315+ for i := 2 ; i < len (notification ); i ++ {
316+ if slotRange , ok := notification [i ].(string ); ok {
317+ slotRanges = append (slotRanges , slotRange )
318+ }
319+ }
320+
321+ if handlerCtx .Conn == nil {
322+ internal .Logger .Printf (ctx , logs .NoConnectionInHandlerContext ("SMIGRATING" ))
323+ return ErrInvalidNotification
324+ }
325+
326+ conn , ok := handlerCtx .Conn .(* pool.Conn )
327+ if ! ok {
328+ internal .Logger .Printf (ctx , logs .InvalidConnectionTypeInHandlerContext ("SMIGRATING" , handlerCtx .Conn , handlerCtx ))
329+ return ErrInvalidNotification
330+ }
331+
332+ // Apply relaxed timeout to this specific connection
333+ if internal .LogLevel .InfoOrAbove () {
334+ internal .Logger .Printf (ctx , logs .SlotMigrating (conn .GetID (), seqID , slotRanges ))
335+ }
336+ conn .SetRelaxedTimeout (snh .manager .config .RelaxedTimeout , snh .manager .config .RelaxedTimeout )
337+ return nil
338+ }
339+
340+ // handleSMigrated processes SMIGRATED notifications.
341+ // SMIGRATED indicates that a cluster slot has finished migrating to a different node.
342+ // This is a cluster-level notification that triggers cluster state reload.
343+ // Expected format: ["SMIGRATED", SeqID, host:port, slot1/range1-range2, ...]
344+ // Note: Multiple connections may receive the same notification, so we deduplicate by SeqID before triggering reload.
345+ // but we still process the notification on each connection to clear the relaxed timeout.
346+ func (snh * NotificationHandler ) handleSMigrated (ctx context.Context , handlerCtx push.NotificationHandlerContext , notification []interface {}) error {
347+ if len (notification ) < 4 {
348+ internal .Logger .Printf (ctx , logs .InvalidNotification ("SMIGRATED" , notification ))
349+ return ErrInvalidNotification
350+ }
351+
352+ // Extract SeqID (position 1)
353+ seqID , ok := notification [1 ].(int64 )
354+ if ! ok {
355+ internal .Logger .Printf (ctx , logs .InvalidSeqIDInSMigratedNotification (notification [1 ]))
356+ return ErrInvalidNotification
357+ }
358+
359+ // Deduplicate by SeqID - multiple connections may receive the same notification
360+ if snh .manager .MarkSMigratedSeqIDProcessed (seqID ) {
361+ // Extract host:port (position 2)
362+ hostPort , ok := notification [2 ].(string )
363+ if ! ok {
364+ internal .Logger .Printf (ctx , logs .InvalidHostPortInSMigratedNotification (notification [2 ]))
365+ return ErrInvalidNotification
366+ }
367+
368+ // Extract slot ranges (position 3+)
369+ // For now, we just extract them for logging
370+ // Format can be: single slot "1234" or range "100-200"
371+ var slotRanges []string
372+ for i := 3 ; i < len (notification ); i ++ {
373+ if slotRange , ok := notification [i ].(string ); ok {
374+ slotRanges = append (slotRanges , slotRange )
375+ }
376+ }
377+
378+ if internal .LogLevel .InfoOrAbove () {
379+ internal .Logger .Printf (ctx , logs .SlotMigrated (seqID , hostPort , slotRanges ))
380+ }
381+ // Trigger cluster state reload via callback, passing host:port and slot ranges
382+ // For now, implementations just log these and trigger a full reload
383+ // In the future, this could be optimized to reload only the specific slots
384+ snh .manager .TriggerClusterStateReload (ctx , hostPort , slotRanges )
385+ }
386+
387+ // clear relaxed timeout
388+ if handlerCtx .Conn != nil {
389+ conn , ok := handlerCtx .Conn .(* pool.Conn )
390+ if ok {
391+ conn .ClearRelaxedTimeout ()
392+ }
393+ }
394+
395+ return nil
396+ }
0 commit comments