@@ -216,16 +216,21 @@ type baseClient struct {
216
216
pushProcessor push.NotificationProcessor
217
217
218
218
// Hitless upgrade manager
219
- hitlessManager * hitless.HitlessManager
219
+ hitlessManager * hitless.HitlessManager
220
+ hitlessManagerLock sync.RWMutex
220
221
}
221
222
222
223
func (c * baseClient ) clone () * baseClient {
224
+ c .hitlessManagerLock .RLock ()
225
+ hitlessManager := c .hitlessManager
226
+ c .hitlessManagerLock .RUnlock ()
227
+
223
228
clone := & baseClient {
224
229
opt : c .opt ,
225
230
connPool : c .connPool ,
226
231
onClose : c .onClose ,
227
232
pushProcessor : c .pushProcessor ,
228
- hitlessManager : c . hitlessManager ,
233
+ hitlessManager : hitlessManager ,
229
234
}
230
235
return clone
231
236
}
@@ -458,7 +463,10 @@ func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
458
463
return fmt .Errorf ("failed to enable maintenance notifications: %w" , hitlessHandshakeErr )
459
464
case hitless .MaintNotificationsAuto :
460
465
// auto mode, disable hitless upgrades and continue
461
- c .disableHitlessUpgrades ()
466
+ if err := c .disableHitlessUpgrades (); err != nil {
467
+ // Log error but continue - auto mode should be resilient
468
+ internal .Logger .Printf (ctx , "hitless: failed to disable hitless upgrades in auto mode: %v" , err )
469
+ }
462
470
c .optLock .RUnlock ()
463
471
c .optLock .Lock ()
464
472
c .opt .HitlessUpgradeConfig .Enabled = hitless .MaintNotificationsDisabled
@@ -663,13 +671,20 @@ func (c *baseClient) enableHitlessUpgrades() error {
663
671
if err != nil {
664
672
return err
665
673
}
666
- // Set the manager reference
674
+ // Set the manager reference and initialize pool hook
675
+ c .hitlessManagerLock .Lock ()
667
676
c .hitlessManager = manager
668
- c .hitlessManager .InitPoolHook (c .dialHook )
677
+ c .hitlessManagerLock .Unlock ()
678
+
679
+ // Initialize pool hook (safe to call without lock since manager is now set)
680
+ manager .InitPoolHook (c .dialHook )
669
681
return nil
670
682
}
671
683
672
684
func (c * baseClient ) disableHitlessUpgrades () error {
685
+ c .hitlessManagerLock .Lock ()
686
+ defer c .hitlessManagerLock .Unlock ()
687
+
673
688
// Close the hitless manager
674
689
if c .hitlessManager != nil {
675
690
// Closing the manager will also shutdown the pool hook
@@ -686,8 +701,14 @@ func (c *baseClient) disableHitlessUpgrades() error {
686
701
// long-lived and shared between many goroutines.
687
702
func (c * baseClient ) Close () error {
688
703
var firstErr error
704
+
705
+ // Close hitless manager first
706
+ if err := c .disableHitlessUpgrades (); err != nil {
707
+ firstErr = err
708
+ }
709
+
689
710
if c .onClose != nil {
690
- if err := c .onClose (); err != nil {
711
+ if err := c .onClose (); err != nil && firstErr == nil {
691
712
firstErr = err
692
713
}
693
714
}
@@ -957,6 +978,8 @@ func (c *Client) Options() *Options {
957
978
// GetHitlessManager returns the hitless manager instance for monitoring and control.
958
979
// Returns nil if hitless upgrades are not enabled.
959
980
func (c * Client ) GetHitlessManager () * hitless.HitlessManager {
981
+ c .hitlessManagerLock .RLock ()
982
+ defer c .hitlessManagerLock .RUnlock ()
960
983
return c .hitlessManager
961
984
}
962
985
@@ -1241,7 +1264,4 @@ func (c *baseClient) pushNotificationHandlerContext(cn *pool.Conn) push.Notifica
1241
1264
}
1242
1265
}
1243
1266
1244
- // initializeHitlessManager initializes hitless upgrade manager for a client.
1245
- func initializeHitlessManager (client * baseClient , config * HitlessUpgradeConfig ) (* hitless.HitlessManager , error ) {
1246
- return nil , nil // TODO: Implement hitless manager initialization
1247
- }
1267
+
0 commit comments