v0.30.0 — waitress thread pool, sticky failure-path conformance
Carries two changes. 0.29.0 was version-bumped on main but never tagged or
published, so its work ships here — PyPI's previous release was 0.28.3.
serve_http can size waitress's thread pool
serve_http built its waitress.serve() call with tuned buffers and I/O chunk
sizes but never passed threads, so every worker it started ran on waitress's
default of 4 — and there was no way to change that short of bypassing
serve_http for make_wsgi_app plus waitress by hand.
Four is tuned for web requests measured in milliseconds. A VGI request can hold
its thread for the length of an entire scan, so the fifth concurrent client
blocks. The failure mode is the bad kind: no error and no log line, just
latency, so it reads as the server being slow rather than the server being full.
New threads parameter, resolving explicit argument → VGI_HTTP_THREADS → 16.
The environment variable is the part that matters in practice: a worker is
almost never launched by something that owns its argv, but by a container, a
process manager, or a test harness. A malformed value exits rather than falling
back, because a silent fallback is indistinguishable from the setting not
working.
Single-client workloads are unaffected — 16 only removes a ceiling that four
concurrent clients were already hitting.
Sticky failure-path conformance, now cross-language
TestSticky gained four cases covering the ways a sticky session must be
refused: a handler exception must not wedge the per-session lock, an expired
token must surface SessionLostError, a token minted by one worker must be
refused by another, and a token replayed under a different principal must be
refused. Principal binding is now normative (sticky-sessions-spec.md §3.1)
rather than merely described.
Three of the four need a worker the default fixture cannot stand in for,
supplied as named runner fixtures and driven by new conformance-worker flags
(--sticky-ttl, --token-key, --sticky-auth). Go, Java, Rust and TypeScript
all supply them, so they are required for any port advertising
VGI-Sticky-Enabled rather than optional; ports without sticky still skip the
group cleanly.
Ports resolving vgi-rpc from PyPI (Rust, TypeScript) will run these
conformance cases for the first time on picking up this release.