Skip to content

perf(vanilla-epoll): serve /static via static_assets (precompressed negotiation)#954

Open
enghitalo wants to merge 2 commits into
MDA2AV:mainfrom
enghitalo:perf/vanilla-epoll-static-precompressed
Open

perf(vanilla-epoll): serve /static via static_assets (precompressed negotiation)#954
enghitalo wants to merge 2 commits into
MDA2AV:mainfrom
enghitalo:perf/vanilla-epoll-static-precompressed

Conversation

@enghitalo

@enghitalo enghitalo commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Problem

vanilla-epoll loses the static profile by a wide margin — and the bandwidth column shows exactly why:

static-4096 rps bandwidth
zix 2.04M 31 GB
vanilla-epoll 1.15M 68 GB

~2× the bandwidth at ~half the rps. The /static handler preloaded only the identity files (it skipped the .br/.gz siblings) and never read Accept-Encoding, so it always shipped the uncompressed body. The profile sends Accept-Encoding: br;q=1, gzip;q=0.8, so the leaders serve the ~4× smaller .br sibling and vanilla was sending the full file.

It was also a second, hand-rolled static path — the lib already ships an audited static_assets module that does precompressed negotiation + ETag/conditional + Vary + sendfile(2) correctly, but the entry didn't use it.

Change

Serve /static/* through static_assets, mounted at /static/ via the new url_prefix (depends on enghitalo/vanilla#80). One audited implementation; drops the duplicate StaticFile / static_header / content_type / C.open. Emits via the same core.queue_file sendfile handoff the worker already drains.

Evidence

Local A/B, server pinned to 2 cores, wrk + this repo's static-rotate.lua (Accept-Encoding: br;q=1), both CPU-saturated:

/static handler req/s Transfer/s
identity-only (current) ~56.8K 3.37 GB/s
static_assets (this PR) ~104K 1.60 GB/s

+83% rps, −52% bandwidth at equal CPU — the precompressed body is the whole difference (mirrors the published zix-vs-vanilla static signature). curl-verified correctness: /static/app.js + Accept-Encoding: brContent-Encoding: br, 47,275 B (vs 204,800 identity); .woff2 (no sibling) → identity; .. traversal → 404; gzip-only → .gz.

Pin

Bumps the vanilla pin to main + enghitalo/vanilla#80 (url_prefix). enghitalo/vanilla#75 (core.queue_buf) and enghitalo/vanilla#79 (kTLS) ride along on main but are inert in this image: it builds without -d vanilla_tls, and queue_buf is unused by the epoll entry. Compiles clean (v -prod -gc none).

Depends on enghitalo/vanilla#80 (the url_prefix field). The Dockerfile pins the branch commit so CI builds today; it lands on vanilla main once enghitalo/vanilla#80 merges.

🤖 Generated with Claude Code

…egotiation)

The /static handler hand-rolled a second static path: it preloaded ONLY the
identity files (skipping the .br/.gz siblings) and never read Accept-Encoding, so
it always shipped the uncompressed body. The static profile sends
`Accept-Encoding: br;q=1, gzip;q=0.8`, so vanilla-epoll was sending ~4x the bytes
of servers that serve the precompressed sibling (≈2x bandwidth, ≈half the rps).

Replace it with the lib's audited static_assets module, mounted at /static/ via
the new `url_prefix` (enghitalo/vanilla#80): one implementation that negotiates
the .br/.gz sibling, sets ETag/Vary/Cache-Control, and streams large assets with
sendfile(2) — emitted through the same core.queue_file handoff the worker already
drains. Drops the duplicate StaticFile/static_header/content_type/C.open.

Local A/B (server pinned to 2 cores, wrk + static-rotate.lua, br;q=1, both CPU-
saturated): identity-only ~56.8K req/s @ 3.37 GB/s  ->  static_assets ~104K req/s
@ 1.60 GB/s (+83% rps, -52% bandwidth) at equal CPU. curl-verified: /static/app.js
+ Accept-Encoding: br -> Content-Encoding: br, 47,275 B (vs 204,800 identity);
.woff2 (no sibling) -> identity; traversal -> 404.

Bumps the vanilla pin to main + MDA2AV#80 (url_prefix). kTLS/MDA2AV#79 and queue_buf/MDA2AV#75 ride
along on main but are inert here (no -d vanilla_tls; queue_buf unused by epoll).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@enghitalo

Copy link
Copy Markdown
Contributor Author

Dependency satisfied: enghitalo/vanilla#80 (url_prefix) is merged, so the pinned commit f59a534 is now on vanilla main. (The borrowed-send emit for small assets — enghitalo/vanilla#81 — is not needed here: it's inert on epoll, which serves the body via out << response either way. It matters for the io_uring sibling, #951.)

…merged)

Both lib deps are now on vanilla main: enghitalo/vanilla#80 (static_assets
url_prefix) and MDA2AV#81 (core.queue_buf borrowed send). Repin from the MDA2AV#80 branch
commit to the merged main commit. No entry change; rebuilds clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@enghitalo

Copy link
Copy Markdown
Contributor Author

/benchmark -f vanilla-epoll

@github-actions

Copy link
Copy Markdown
Contributor

👋 /benchmark request received. A collaborator will review and approve the run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant