Skip to content

fix(router): establish mux legs asynchronously so a dial serves on its primary immediately - #4209

Merged
0pcom merged 1 commit into
skycoin:developfrom
0pcom:fix/async-foreground-mux
Aug 26, 2026
Merged

fix(router): establish mux legs asynchronously so a dial serves on its primary immediately#4209
0pcom merged 1 commit into
skycoin:developfrom
0pcom:fix/async-foreground-mux

Conversation

@0pcom

@0pcom 0pcom commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

The Dial path called establishMuxRoutes synchronously in finishDial, so it blocked until up to initialForegroundMux (16) aux legs finished Phase-1 planning. Each plan hits the route-finder, and when the RF times out the whole dial stalled ~90s before the app (e.g. skysocks-client) could bind and serve — even though the primary route (leg 0) is ready in ~1s.

This runs the slow leg establishment in the background. The fast, non-dialing work stays synchronous: computing muxTarget + the same-LAN/control-plane no-mux guards, and wiring the leg-change / self-heal / rotation callbacks (they only install callbacks, no dialing). The slow part — establishMuxRoutes, then applyDistribution, then maybeSelfHeal — moves into a goroutine on a fresh 5-minute background context (the dial's ctx may be canceled once Dial returns). Ordering is preserved so the distribution rebuild still sees every leg.

Dial now returns as soon as the primary route group is up; the mux legs fill in behind it. The no-mux branch keeps applyDistribution synchronous. Full router suite stays green.

…s primary immediately

The Dial path called establishMuxRoutes synchronously in finishDial, so it
blocked until up to initialForegroundMux (16) aux legs finished Phase-1
planning. Each plan hits the route-finder, and when the RF times out the
whole dial stalled ~90s before the app could bind and serve — even though the
primary route (leg 0) is ready in ~1s. Now the fast, non-dialing work stays
synchronous (compute muxTarget + same-LAN/control-plane guards, wire the
leg-change / self-heal / rotation callbacks) and the slow leg establishment —
establishMuxRoutes, then applyDistribution, then maybeSelfHeal — runs in a
goroutine on a fresh 5-minute background context (the dial ctx may be canceled
once Dial returns). Dial returns as soon as the primary route group is up; the
mux legs fill in behind it. The no-mux branch keeps applyDistribution
synchronous.
@0pcom
0pcom merged commit 10285a9 into skycoin:develop Aug 26, 2026
11 of 15 checks passed
0pcom added a commit that referenced this pull request Aug 26, 2026
…rsification (#4216)

Keep N healthy tunnels in the multi-tunnel skysocks client so the aggregation
width does not bleed down over time. Previously the keepalive loop only SKIPPED
a dead tunnel (pickSession routes around it); it never replaced one, so a
long-lived --tunnels N client decayed toward a single tunnel and lost the
bandwidth aggregation.

The Client now stores the target N and, when a tunnel dies and the live count
falls below N (but at least one survives), re-dials a fresh DISJOINT replacement
via a callback the app wires in (SetTunnelRedial closes over the same
diversify=true dialServer path the initial extra tunnels use, so the visor steers
the replacement off the survivors' first-hop transports, #4214). Re-dial is
bounded: a single in-flight attempt (atomic CAS guard) and a back-off after
consecutive failures until the next tunnel death re-arms it, so a persistently
unreachable exit does not spin. N==1 never re-dials — its lone tunnel's death is
total collapse, still owned by the app's --reconnect runCycle — so the default
path is byte-identical.

Sequential diversification is reliable without a router change or an added delay:
the visor registers each tunnel's primary route group (with its first-hop
transport) into rgsNs synchronously inside saveRouteGroupRules, before the dial
RPC returns; #4209 defers only auxiliary mux legs, which skysocks tunnels never
request. Because the extra-tunnel loop dials sequentially, tunnel i is always
visible to tunnel i+1's sibling-exclusion scan. Documented at both sites.

Throughput-based eviction of a slow-but-alive tunnel (the "drop the
underperforming" half of RFC step 4) is deferred: it needs the gigabit
validation rig to tune the slow-leg threshold. This change is liveness-only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant