feat(router): instant warm-standby failover so a leg death never dead-ends a connection - #4207
Merged
Merged
Conversation
…-ends a connection The warm-standby reserve (now 512 deep) only got switched in on the periodic rotation tick — up to a full rotation interval (~20s) after an active leg died. For a lean active set that meant a real dead-connection window, defeating the point of a 'switch in at a moment's notice' reserve. Two changes make failover instant, both leaving the rotation interval itself untouched (its cadence still governs UDP/sudph keep-alive and anti-churn): - Selector emergency fallback (route_mux.go): when selectTransport finds NO active leg selectable (all dead/not-ready), it now falls through to any alive, ready warm-standby leg instead of returning ErrNoSuitableTransport. A parked leg keeps its rules installed and its transport alive, so it carries the packet immediately — the connection survives as long as ANY of the group's legs is alive, with zero promote latency. legSelectableIgnoringStandby is legReadyAt without the standby exclusion, so an unconfirmed leg is still never used. - Event-driven promote (route_group.go): a leg death now signals the rotation loop (rotateNow, buffered-1, non-blocking) to run its on_tick controller AT ONCE, so drop-recovery promotes a warm standby into the active set immediately rather than on the next interval. servicePacketLoop gained an optional trigger channel (nil for the non-rotation loops, so they are unchanged). Together: the selector keeps traffic flowing the instant the active set is lost, and the immediate tick restores a proper active set right after. Full router suite green; new test covers legSelectableIgnoringStandby.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The warm-standby reserve (now 512 deep) only switched in on the periodic rotation tick — up to a full interval (~20s) after an active leg died. For a lean active set that's a real dead-connection window, defeating a 'switch in at a moment's notice' reserve.
Two changes, both leaving the rotation interval itself untouched (its cadence still governs UDP/sudph keep-alive + anti-churn):
route_mux.go): whenselectTransportfinds no active leg selectable (all dead/not-ready), it falls through to any alive, ready warm-standby leg instead of failing the send. A parked leg keeps its rules + transport alive, so it carries the packet immediately — the connection survives as long as any leg is alive, zero promote latency.route_group.go): a leg death signals the rotation loop (rotateNow, buffered-1, non-blocking) to run its controller at once, so drop-recovery promotes a standby immediately.servicePacketLoopgained an optional trigger channel (nil for other loops).Full router suite green; new test covers
legSelectableIgnoringStandby.