vanilla: DB path — prepared statement (async-db) + single-pass HTML escape#878
Closed
enghitalo wants to merge 1 commit into
Closed
vanilla: DB path — prepared statement (async-db) + single-pass HTML escape#878enghitalo wants to merge 1 commit into
enghitalo wants to merge 1 commit into
Conversation
… escape Two DB-path improvements (both backends): 1. async-db now uses a PostgreSQL prepared statement (PQprepare/PQexecPrepared via db.pg) instead of exec_param_many (PQexecParams), which re-parses the SQL server-side on every request. Lazily prepared on each pooled connection's first use — prepared statements are per-session and the pool reuses sessions, so after warmup every connection serves exec_prepared. 2. escape_html (fortunes) does ONE pass with a fast path (return the message untouched, no allocation, when it has no special chars) instead of replace_each's five full-string scans + reallocations. Local before/after (16-core loopback, gcannon, single listener): fortunes ~5.6K -> ~7.1K req/s (+27%, from the escape rewrite) async-db ~18.0K -> ~19.6K req/s (+9%, from the prepared statement) These are incremental: the DB profiles remain bound by the stdlib db.pg driver (text protocol, lazy pool) — the top DB engines use native binary-protocol drivers — so this narrows the gap without closing it. Correctness verified (async-db rows + count correct; fortunes renders the same rows). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Author
|
/benchmark -f vanilla-epoll --save |
Contributor
|
👋 |
Contributor
Author
|
Folded into #877 so everything lands in a single PR (json-comp cache + zero-alloc routing/keys + DB prepared statement + single-pass HTML escape). |
Contributor
Benchmark ResultsFramework:
Full log |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two DB-path improvements in
frameworks/vanilla-{epoll,io_uring}/main.v:exec_param_many(PQexecParams) re-parses the SQL server-side on every request. Switched to a PostgreSQL prepared statement (PQprepare+PQexecPreparedviadb.pg), lazily prepared on each pooled connection's first use — prepared statements are per-session and the pool reuses sessions, so after warmup every connection servesexec_prepared.escape_htmldid five full-stringreplace_eachpasses; now it's one pass with a fast path that returns the message untouched (no allocation) when there's nothing to escape.Measured (local, 16-core loopback, gcannon, single listener)
Honest scope
These are incremental. The DB profiles remain bound by the stdlib
db.pgdriver (text protocol, lazy pool) — confirmed byveb(same V +db.pg) being slower than vanilla on async-db. The top DB engines (swerver, genhttp) use native binary-protocol drivers with persistent prepared statements + pipelining; closing that gap needs a faster pg driver (a separate, larger project), not framework tweaks. Correctness verified (async-db rows + count; fortunes renders the same rows). Both epoll and io_uring variants.🤖 Generated with Claude Code