Skip to content

rust/wasmtime: set TCP_NODELAY on the connected socket - #45

Merged
lann merged 1 commit into
mainfrom
wasmtime-tcp-nodelay
Aug 11, 2026
Merged

rust/wasmtime: set TCP_NODELAY on the connected socket#45
lann merged 1 commit into
mainfrom
wasmtime-tcp-nodelay

Conversation

@lann

@lann lann commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Latency-sensitive guests (QUIC over the message stream, per component-iroh) write small messages back-to-back; Nagle holds the second write until the peer's delayed ACK arrives — a ~40 ms stall per occurrence on Linux loopback. Browsers run WebSocket sockets with TCP_NODELAY, so the wasmtime host is the deficient implementation on the portability ladder; this enhances it to match.

Found driving component-iroh's endpoint to event-driven wakeups (polymorph-components/polymorph-iroh#42): with guest flushes no longer batched by a 10 ms clock tick, back-to-back small sends surfaced the stall — relay-wire echo roundtrips sat at 27–50 ms and drop to 0–7 ms with this change (numbers from component-iroh's bench rig; its budget gate re-asserts them once its websocket pin bumps).

The wss: (Rustls) arm is covered by reaching through MaybeTlsStream::Rustls; the catch-all arm covers the enum's non-exhaustive future variants by leaving them as-is.

Verified: just check; just conformance-ct — 4 targets, 220 results, 0 failing.

Latency-sensitive guests (QUIC over the message stream, per
component-iroh) write small messages back-to-back; Nagle holds the
second write until the peer's delayed ACK arrives, a ~40 ms stall per
occurrence on Linux loopback. Browsers run WebSocket sockets with
TCP_NODELAY, so the wasmtime host was the deficient implementation on
the portability ladder (enhance, never accumulate divergence).

Found driving component-iroh's endpoint to event-driven wakeups
(polymorph-components/polymorph-iroh#42): with flushes no longer
batched by a 10 ms clock tick, back-to-back small sends surfaced the
stall — relay-wire echo roundtrips sat at 27-50 ms and drop to 0-7 ms
with this change.

Verified: just check; just conformance-ct (4 targets, 220 results, 0
failing).
@lann
lann merged commit f8fdf66 into main Aug 11, 2026
2 checks passed
@lann
lann deleted the wasmtime-tcp-nodelay branch August 11, 2026 00:39
lann added a commit to polymorph-components/polymorph-iroh that referenced this pull request Aug 11, 2026
With host-jco retired (#40), the portable workaround for its scheduler
defect - bounded clock polling (5 ms method quantum / 10 ms pump tick)
- protected nothing and cost ~5x handshake latency on every host.

Wake-ups are now event-driven in both directions, riding wit-bindgen's
inter-task-wakeup channel (both hosts deliver it): resource methods
kick the pump to flush their mutations (first flight, stream writes,
FIN/RESET/STOP_SENDING, datagrams, signals, closes, flow-control
credit) and park on wakers in State::waiters; the pump wakes the
waiters after every drain that progressed, on signal-inbox pushes, and
unconditionally on its 10 ms tick, which stays for noq's timers and
now also bounds deadline re-checks and any missed wake edge. The
never-cancel-an-import teardown discipline is unchanged; the kick
future is guest-local.

Going event-driven exposed a latent Nagle stall in the websocket
sibling's wasmtime host: back-to-back small sends (STREAM then FIN,
no longer batched by the tick) sat out the peer's delayed ACK, putting
relay-wire echo roundtrips at 27-50 ms. Fixed upstream
(polymorph-components/polymorph-websocket#45, TCP_NODELAY); the
websocket pin bumps to that commit here. The two changes are
co-dependent: the pump change without the pin bump regresses relay
roundtrips 23 -> ~47 ms.

Bench on this machine (medians): endpoint handshakes 16/21/14 ms
(relay/udp/webrtc) -> 1-4 ms, matching the spike; roundtrips 23/17/23
-> 0 ms; bulk throughput 7.7/49.1/38.1 -> 27.8/85.1/49.1 MB/s (prompt
flow-control credit plus nodelay). The budgets tighten accordingly:
absolute ceilings 2000 -> 250 ms, and the spike-to-endpoint handshake
delta is asserted (<= 10 ms) so the polling tax cannot quietly return.

Verified: just check, just probes (5/5), just matrix (12 pairings),
just exam-deltic (5/5), just bench (budgets hold, handshake_tax_ms 0).

Fixes #42
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