@@ -216,16 +216,21 @@ type baseClient struct {
216216 pushProcessor push.NotificationProcessor
217217
218218 // Hitless upgrade manager
219- hitlessManager * hitless.HitlessManager
219+ hitlessManager * hitless.HitlessManager
220+ hitlessManagerLock sync.RWMutex
220221}
221222
222223func (c * baseClient ) clone () * baseClient {
224+ c .hitlessManagerLock .RLock ()
225+ hitlessManager := c .hitlessManager
226+ c .hitlessManagerLock .RUnlock ()
227+
223228 clone := & baseClient {
224229 opt : c .opt ,
225230 connPool : c .connPool ,
226231 onClose : c .onClose ,
227232 pushProcessor : c .pushProcessor ,
228- hitlessManager : c . hitlessManager ,
233+ hitlessManager : hitlessManager ,
229234 }
230235 return clone
231236}
@@ -458,7 +463,10 @@ func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
458463 return fmt .Errorf ("failed to enable maintenance notifications: %w" , hitlessHandshakeErr )
459464 case hitless .MaintNotificationsAuto :
460465 // 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+ }
462470 c .optLock .RUnlock ()
463471 c .optLock .Lock ()
464472 c .opt .HitlessUpgradeConfig .Enabled = hitless .MaintNotificationsDisabled
@@ -663,13 +671,20 @@ func (c *baseClient) enableHitlessUpgrades() error {
663671 if err != nil {
664672 return err
665673 }
666- // Set the manager reference
674+ // Set the manager reference and initialize pool hook
675+ c .hitlessManagerLock .Lock ()
667676 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 )
669681 return nil
670682}
671683
672684func (c * baseClient ) disableHitlessUpgrades () error {
685+ c .hitlessManagerLock .Lock ()
686+ defer c .hitlessManagerLock .Unlock ()
687+
673688 // Close the hitless manager
674689 if c .hitlessManager != nil {
675690 // Closing the manager will also shutdown the pool hook
@@ -686,8 +701,14 @@ func (c *baseClient) disableHitlessUpgrades() error {
686701// long-lived and shared between many goroutines.
687702func (c * baseClient ) Close () error {
688703 var firstErr error
704+
705+ // Close hitless manager first
706+ if err := c .disableHitlessUpgrades (); err != nil {
707+ firstErr = err
708+ }
709+
689710 if c .onClose != nil {
690- if err := c .onClose (); err != nil {
711+ if err := c .onClose (); err != nil && firstErr == nil {
691712 firstErr = err
692713 }
693714 }
@@ -957,6 +978,8 @@ func (c *Client) Options() *Options {
957978// GetHitlessManager returns the hitless manager instance for monitoring and control.
958979// Returns nil if hitless upgrades are not enabled.
959980func (c * Client ) GetHitlessManager () * hitless.HitlessManager {
981+ c .hitlessManagerLock .RLock ()
982+ defer c .hitlessManagerLock .RUnlock ()
960983 return c .hitlessManager
961984}
962985
@@ -1241,7 +1264,4 @@ func (c *baseClient) pushNotificationHandlerContext(cn *pool.Conn) push.Notifica
12411264 }
12421265}
12431266
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