Skip to content

v2.4.1

Choose a tag to compare

@dancixx dancixx released this 11 Oct 18:05
· 76 commits to main since this release
974479f

Tako 2.4.1 fixes three throughput problems that running Tako in HttpArena brought to light: extracting State<T> serialized every request on one lock, buffered brotli compression allocated a 4 MiB window for each response, and static files took four blocking-pool round trips per request. It also lets Query<T> read a request that has no query string.

No code changes are needed. All eight workspace crates are versioned together at 2.4.1 and require Rust 1.95 or newer.

Performance

  • State<T> and get_state read under a shared lock. They looked the value up with scc::HashMap::get_sync, which holds the bucket's exclusive lock, so every request that extracted state queued on the same lock, and a per-thread worker waiting for it stalled every connection it served.
  • Buffered brotli compression writes the whole body in one call with a size hint. Fed in 4 KiB pieces, the encoder allocated its full window: 9.7 MB for an 8 KB JSON response, against 1.24 MB now. The compressed bytes are identical.
  • ServeDir and ServeFile on Tokio resolve, open, and read a file of up to 128 KiB in one blocking-pool call and send it as one buffer. Larger files still stream in 64 KiB chunks, and Compio is unchanged.

Requests per second, the median of three 10-second wrk runs in a 48 vCPU container on an Intel Xeon host, with the server on 24 vCPUs and wrk on the other 24:

Workload 2.4.0 2.4.1
JSON over TLS, 4,096 connections 21,978 311,605
Static files over TLS with brotli sidecars, 1,024 connections 30,316 107,545
Brotli-compressed JSON, 4,096 connections 8,156 62,194
Plain-text GET on the per-thread server, 4,096 connections 950,387 972,436

The compressed JSON row comes from a separate run on the same kind of host.

Fixed

  • Query<T> deserializes a request without a query string as an empty query, so a struct whose fields are all Option extracts instead of failing with 400. A struct with a required field still gets QueryError::MissingQueryString.

Full diff: v2.4.0...v2.4.1