MarkQueuedForHandoff marks the connection as queued for handoff processing. This makes the connection unusable until handoff completes. This is called from OnPut hook, where the connection is typically in IN_USE state. The pool will preserve the UNUSABLE state and not overwrite it with IDLE.
()
| 735 | // This is called from OnPut hook, where the connection is typically in IN_USE state. |
| 736 | // The pool will preserve the UNUSABLE state and not overwrite it with IDLE. |
| 737 | func (cn *Conn) MarkQueuedForHandoff() error { |
| 738 | // Get current handoff state |
| 739 | currentState := cn.handoffStateAtomic.Load() |
| 740 | if currentState == nil { |
| 741 | return errNotMarkedForHandoff |
| 742 | } |
| 743 | |
| 744 | state := currentState.(*HandoffState) |
| 745 | if !state.ShouldHandoff { |
| 746 | return errNotMarkedForHandoff |
| 747 | } |
| 748 | |
| 749 | // Create new state with ShouldHandoff=false but preserve endpoint and seqID |
| 750 | // This prevents the connection from being queued multiple times while still |
| 751 | // allowing the worker to access the handoff metadata |
| 752 | newState := &HandoffState{ |
| 753 | ShouldHandoff: false, |
| 754 | Endpoint: state.Endpoint, // Preserve endpoint for handoff processing |
| 755 | SeqID: state.SeqID, // Preserve seqID for handoff processing |
| 756 | } |
| 757 | |
| 758 | // Atomic compare-and-swap to update state |
| 759 | if !cn.handoffStateAtomic.CompareAndSwap(currentState, newState) { |
| 760 | // State changed between load and CAS - retry or return error |
| 761 | return errHandoffStateChanged |
| 762 | } |
| 763 | |
| 764 | // Transition to UNUSABLE from IN_USE (normal flow), IDLE (edge cases), or CREATED (tests/uninitialized) |
| 765 | // The connection is typically in IN_USE state when OnPut is called (normal Put flow) |
| 766 | // But in some edge cases or tests, it might be in IDLE or CREATED state |
| 767 | // The pool will detect this state change and preserve it (not overwrite with IDLE) |
| 768 | // Use predefined slice to avoid allocation |
| 769 | finalState, err := cn.stateMachine.TryTransition(validFromCreatedInUseOrIdle, StateUnusable) |
| 770 | if err != nil { |
| 771 | // Check if already in UNUSABLE state (race condition or retry) |
| 772 | // ShouldHandoff should be false now, but check just in case |
| 773 | if finalState == StateUnusable && !cn.ShouldHandoff() { |
| 774 | // Already unusable - this is fine, keep the new handoff state |
| 775 | return nil |
| 776 | } |
| 777 | // Restore the original state if transition fails for other reasons |
| 778 | cn.handoffStateAtomic.Store(currentState) |
| 779 | return fmt.Errorf("failed to mark connection as unusable: %w", err) |
| 780 | } |
| 781 | return nil |
| 782 | } |
| 783 | |
| 784 | // GetID returns the unique identifier for this connection. |
| 785 | func (cn *Conn) GetID() uint64 { |