perf(net): disable Nagle on the API server, proxy and envd clients - #109
Merged
Conversation
envd streams a command's lifecycle as a burst of tiny Connect-RPC frames. With Nagle enabled, every frame after the first waits for the peer's delayed ACK, which puts a ~40ms floor under each short-lived command regardless of how long the command itself runs. Every hop on that path exchanges small frames only, so the coalescing Nagle trades latency for never materializes. Set TCP_NODELAY on the API server's accepted connections, on the sandbox proxy's connector and on both envd client connectors.
12 tasks
Contributor
|
✅ OpenCodeReview: Review complete: 0 finding(s) across 3 selected item(s). |
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.
What
Set
TCP_NODELAYon every hop of the sandbox command path: the API server's accepted connections, the sandbox proxy's connector, and both envd client connectors (h1 and h2).Why
Short-lived commands executed through a sandbox had a hard ~40 ms latency floor that was independent of how long the command itself ran.
envd streams a command's lifecycle as a burst of tiny Connect-RPC frames. With Nagle enabled, the sender holds the second small frame until the peer acknowledges the first, and the peer's kernel delays that ACK by ~40 ms. Every hop on this path exchanges small frames only, so the coalescing win Nagle trades latency for never materializes.
Packet captures showed exactly one ~40 ms gap per command, and the first packet after the gap was a bare ACK from the client back to the node — the signature of delayed ACK meeting Nagle. The effect is a floor rather than an additive delay:
A minimal two-small-writes-on-one-connection reproduction confirms the mechanism and the fix:
TCP_NODELAYoffTCP_NODELAYonThe leading and trailing zeros are the kernel's quickack window, which also explains the reported "fast after idle, slow when run back to back" behavior.
Related issue
None. Submitted directly per CONTRIBUTING: the scope is a socket option on an existing code path with no API or format impact. Happy to open an issue first if maintainers prefer.
Scope and non-goals
Included:
TCP_NODELAYat the three places that carry envd RPC traffic.Excluded:
tokio-tungstenitealready sets the option.Design and behavior changes
src/bin/server.rsusesaxum::serve::ListenerExt::tap_ioto set the option on each accepted connection; a failure is logged atwarnand the connection proceeds, since a missing socket option is a latency problem rather than a correctness one.src/api/proxy.rsandthirdparty/envd/src/transport.rsset it on theirHttpConnectors. The envd change is factored into a smallnodelay_connector()helper because both the h1 and h2 clients need it.No change to protocol framing, ordering, or error handling. Disabling Nagle can increase packet count for senders that write many small chunks, which is precisely the traffic pattern being fixed here, and these are short-lived localhost-to-VM connections.
Compatibility and operations
axum'sListenerExtis already available in the pinned version.Validation
make fmtmake clippymake test-unitmake -C services test(required whenservices/changes)maketargetCommands and results:
Skipped checks and reasons:
make test-unitnot run: no existing unit test observes socket options, and this change adds no branch to test. The behavior is only observable at the TCP layer, which the reproduction above covers./dev/kvm, and a provisioned host; unavailable in the environment used here.scripts/bench.pyor by timing a trivial command over the API before and after.Risks and reviewer notes
Low risk. The main reviewer question is whether disabling Nagle is right for all proxied traffic rather than just the RPC control frames. It is: the proxy carries sandbox HTTP traffic over short-lived localhost-to-VM connections, where the bandwidth saving from coalescing is irrelevant and the latency cost is paid on every request.
Second question:
tap_iofailures are swallowed with awarn. Failing the connection instead would turn a performance regression into an outage, which seems worse.Most important file:
src/bin/server.rs.Checklist