Skip to content

Autoconnect: deprioritize WebRTC transports (~24-28 goroutines each vs ~1 for STCPR) #3941

Description

@0pcom

Summary

A live goroutine profile of a public visor holding 989 transports to 480 peers (STCPR 432, SQUICR 338, SUDPH 191, WEBRTC 20, SWTR 8) shows WebRTC is by far the most resource-expensive carrier per transport:

Type Goroutines / transport Notes
STCPR / STCP ~1 just the universal managedTransport.readLoop; plain TCP needs no helper — cheapest
DMSG ~1 + shared sessions dmsg sessions to servers are shared/amortized across all dmsg streams
SUDPH ~3 readLoop + reliable-UDP layer over a shared udpdemux
SQUICR / SWTR ~3-4 readLoop + quic-go per-Connection machinery; shared unified QUIC listener
WEBRTC ~24-28 readLoop + pion ICE agent + DTLS + SCTP (~20-24) + media (~4, removed in #3940) — ~20x a TCP transport

Baseline is exactly 1 goroutine/transport (989 readLoop for 989 transports). Everything above that is carrier-specific. Memory footprint tracks the same ranking (pion allocates ICE candidate tables, DTLS buffers, SCTP reassembly queues; TCP is just the socket + noise buffers).

On a webrtc-heavy exit — e.g. a resource-constrained ARM board — a few hundred webrtc transports means several thousand parked goroutines competing for the CPU the route-setup handshake needs.

Proposal

Adjust autoconnect / transport-type selection to prefer the cheap directly-reachable carriers (STCPR, SQUICR, SUDPH) and treat WebRTC as a fallback reserved for where it's actually required — NAT traversal the others can't achieve, and browser (wasm-visor) reachability. Where a peer is reachable via STCPR/SQUICR/SUDPH, autoconnect shouldn't also stand up a WebRTC transport to it.

Related: reduce per-WebRTC-transport cost where WebRTC is used

pkg/transport/network/webrtc_native.go currently sets only DetachDataChannels() (+ the empty MediaEngine from #3940). Bigger structural levers remain:

  • Shared ICE UDPMux (SettingEngine.SetICEUDPMux): one shared UDP socket + one read loop for all PeerConnections instead of per-conn gathering — collapses the largest chunk of the ~24 ICE goroutines into a constant. Standard pion server-scaling technique.
  • ICE-lite on public visors (SettingEngine.SetLite(true)): a directly-reachable node can skip the full ICE connectivity-check state machine, shedding most ICE agent goroutines. Gate to public nodes only.
  • Restrict gathering (SetNetworkTypes([UDP4]), ensure mDNS disabled) for fewer gather goroutines.

Context

Found while diagnosing the wasm (browser) visor wedging on per-exit DMSG-relay accumulation (fixed in #3939). The autoconnect side of the same theme — don't pile on expensive transport types when cheap ones reach the peer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions