Repository navigation
v2.4.1
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>andget_stateread under a shared lock. They looked the value up withscc::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.
ServeDirandServeFileon 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 allOptionextracts instead of failing with 400. A struct with a required field still getsQueryError::MissingQueryString.
Full diff: v2.4.0...v2.4.1