From 8687e4ff1b9412ce88ce080da4443eb8a7c7f2cd Mon Sep 17 00:00:00 2001 From: rays Date: Fri, 9 Oct 2026 13:40:46 +0000 Subject: [PATCH] Turn Nagle's algorithm off on the web server's connections (#170) axum 0.7 set TCP_NODELAY on every accepted connection; 0.8 stopped, and serve never set it, so each response sent in parts held its last small segment until the browser's delayed ACK came back. On production's light traffic nothing showed: small responses go out in one write. Under load it did. tests/load, Nagle on then off: media range requests median 41.8 ms to 10.3 ms, p95 44.4 to 13.2, throughput 39 to 149 MB/s. Browse and sign-in unchanged within noise; flag saves vary 80-195 ms either way, behind the scan's writes, not the network. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + src/web.rs | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bde06d..1db3c55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Cover art up to 20 MB is shown, instead of nothing for anything over 5 MB, as for five of the Glass Cannon Network's feeds. +- Seeking in an episode while many people listen answers in about 10 ms instead of 40, and files stream about four times as fast: each connection now sends what is ready without waiting for the browser to acknowledge the last part. - Artwork that could not be fetched is not fetched again for an hour, instead of on every page load, and the server's log says which image it was and why. ## [0.10.3] - 2026-10-09 diff --git a/src/web.rs b/src/web.rs index 6010fc6..871a63d 100644 --- a/src/web.rs +++ b/src/web.rs @@ -91,9 +91,17 @@ pub fn router(state: WebState) -> Router { } pub async fn serve(state: WebState, bind: &str) -> Result<()> { + use axum::serve::ListenerExt; + // Nagle off, as axum 0.7 had it: under load a response goes out in parts, and with Nagle + // on its last small part waits for the browser's delayed ACK, up to 40 ms (#170). let listener = tokio::net::TcpListener::bind(bind) .await - .with_context(|| format!("binding {bind}"))?; + .with_context(|| format!("binding {bind}"))? + .tap_io(|tcp| { + if let Err(e) = tcp.set_nodelay(true) { + tracing::debug!(error = %e, "could not set TCP_NODELAY"); + } + }); tracing::info!(bind, "web ui listening"); axum::serve( listener,