# TechEmpower FrameworkBenchmarks entries for mq-bridge
Benchmark entries shaped like the [TechEmpower FrameworkBenchmarks](https://github.com/TechEmpower/FrameworkBenchmarks)
project, for both the Rust core (`mq-bridge`) and the Python bindings
(`mq-bridge-py`).
> **Status:** TechEmpower FrameworkBenchmarks is now archived (read-only), so
> these are no longer submitted upstream — they live on as a **verified local
> conformance + throughput harness** (`verify.sh`). For maintained public
> benchmark suites, see the sibling entries under
> [`scripts/the-benchmarker/`](../the-benchmarker/) (the-benchmarker/web-frameworks)
> and [`scripts/httparena/`](../httparena/) (HttpArena).
## Scope
| JSON | `GET /json` | Rust + Python | `{"message":"Hello, World!"}` serialized **per request** |
| Plaintext | `GET /plaintext` | Rust + Python | `Hello, World!` (the pipelined throughput test) |
| Single Query | `GET /db` | Rust only | one random `World` row as JSON |
| Multiple Queries| `GET /queries?queries=N` | Rust only | N random `World` rows (N clamped 1..500) |
Fortunes (HTML templating) and Data Updates are intentionally not implemented —
mq-bridge is a message-bridging library, not a full-stack web framework. The DB
tests are included for the Rust entry as a SQLx/Postgres example (see below);
they are omitted from the Python entry on purpose.
## How it works
Both entries listen on `0.0.0.0:8080`. The shared HTTP server keys on the listen
address, so multiple routes (or, for Python, the single route) share one
process/port.
- **Rust entry: two routes.** A path-filtered `GET /plaintext -> static` route
answers the Plaintext test **handler-free**, and a catch-all `http -> response`
route dispatches the rest (`/json`, `/db`, `/queries`) on the request's
`http_path` metadata inside the handler. The router prefers the more specific
(path-filtered) route, so `/plaintext` hits the static reply and everything
else falls through to the handler.
- **Python entry: one route.** A single `http -> response` route with no path
filter; the handler dispatches both `/json` and `/plaintext` on `http_path`.
(The static path is intentionally not used here — see "On bypassing the
handler" below.)
Key properties (see the inline comments in each entry):
- **Inline fast path.** Both `http -> response` and `http -> static` routes reply
inline, bypassing the route worker/disposition pipeline. So route
`concurrency`/`batch_size` do *not* gate the hot path; we use `concurrency: 1`.
Per-connection parallelism comes from the Tokio runtime spawning a task per
accepted connection.
- **Off-GIL work (Python).** All HTTP framing and JSON (de)serialization run in
Rust off the GIL; the single Python worker thread only runs the trivial
dispatch. This is the differentiator that should place `mq-bridge-py` near the
top of the Python-language entries on the JSON test.
- **`concurrency_limit`.** A per-request semaphore (`acquire_owned` per request,
default 100). We raise it to 65536 so it never throttles TechEmpower's many
connections; the acquire cost itself is inherent to the current server.
- **Buffered pipelined writes.** The core HTTP server enables hyper's
`pipeline_flush(true)`, which coalesces the responses of pipelined HTTP/1.1
requests into a single buffered write — the throughput win for the Plaintext
test, and a no-op for non-pipelined traffic.
- **Headers.** The handler's returned message metadata becomes response headers
(`Content-Type`, `Server`); hyper adds `Date` and `Content-Length`. The status
comes from `http_status_code` metadata (default 200).
## Database tests (Rust entry)
`/db` and `/queries` run `SELECT id, randomnumber FROM world WHERE id = $1` for a
random id (1..10000) and return the row(s) as JSON (`{"id":..,"randomNumber":..}`).
Why a handler-owned `sqlx::PgPool` rather than mq-bridge's `sqlx` endpoint: that
endpoint models a table as a **message queue** — the publisher does `INSERT`, the
consumer runs a *polling* `SELECT` whose only bind is the route `batch_size` and
emits rows as a stream of messages. It has no per-request parameter and no
request↔reply correlation, so it cannot serve TechEmpower's random-id,
reply-synchronously pattern. The handler owns a pool and runs the query directly.
The pool is optional: without a reachable `DATABASE_URL` the DB routes return 503
and `/json` + `/plaintext` still run. The Docker image points `DATABASE_URL` at
TechEmpower's `tfb-database` host.
**Not added to the Python entry on purpose.** The Python handler runs
synchronously on the single Python worker thread, so a Python DB driver there
would serialize every query through one thread and badly misrepresent the
framework. The Python entry's value is off-GIL JSON serialization, not DB I/O.
## On bypassing the handler (switch + static)
mq-bridge has a `static` endpoint (emits a fixed response body) and a `switch`
endpoint (routes on a metadata key), so `http → switch on http_path → static`
(or just `http → static`) can answer with **no handler at all**, replying inline
on the HTTP fast path.
The `static` endpoint supports the two properties this needs:
1. **`raw: true`** sends the body verbatim (so `Hello, World!` is not JSON-quoted), and
2. **`metadata:`** entries are attached to the response; for an HTTP response
they become headers — set `content-type` (and optionally `server`) here so the
reply doesn't default to `application/octet-stream`.
```yaml
plaintext:
input: { http: { url: "0.0.0.0:8080", path: "/plaintext" } }
output:
static:
body: "Hello, World!"
raw: true
metadata:
content-type: "text/plain"
```
For **plaintext** this is the only test where a static (handler-free) reply is
allowed by the rules, and it shaves the per-request handler hop. **The Rust entry
uses it** (the `GET /plaintext -> static` route above). The **Python entry does
not**: there the plaintext reply stays on the trivial handler. Routing Python
plaintext through `static` would mean running a second handler-free route
alongside the JSON handler route — and since the Python `Route.run()` owns its
own runtime, that means two runtimes against the one process-global HTTP server.
It's viable but unverified, and the Python entry's headline result is off-GIL
JSON throughput, not plaintext — so it's kept simple on purpose.
For **JSON** the static path is *not* an option for either entry: the rules
require per-request serialization, not a pre-rendered string — the handler is the
conformant choice there.
## Layout
```text
scripts/techempower/
verify.sh # local conformance + optional wrk read
postgres.yml # local Postgres for the /db tests (not submitted)
seed.sql # World table seed (10,000 rows)
Rust/mq-bridge/ # Rust entry (cargo binary)
README.md # framework README submitted upstream
src/main.rs
Cargo.toml # path-dep on this repo; swap to git tag for the PR
benchmark_config.json
mq-bridge.dockerfile
Python/mq-bridge-py/ # Python entry
README.md # framework README submitted upstream
server.py
benchmark_config.json
mq-bridge-py.dockerfile
```
This top-level README is the developer overview and is **not** submitted
upstream. The per-framework `README.md` files inside `Rust/mq-bridge/` and
`Python/mq-bridge-py/` are the ones TechEmpower expects (one per entry) and get
copied with their directories.
## Verify locally
```bash
# Rust: builds and checks /json + /plaintext conform, then a quick wrk read if present
scripts/techempower/verify.sh rust
# Rust including the DB tests: bring up local Postgres, then point DATABASE_URL at it
docker compose -f scripts/techempower/postgres.yml up -d
DATABASE_URL="postgres://benchmarkdbuser:benchmarkdbpass@127.0.0.1:5433/hello_world" \
scripts/techempower/verify.sh rust
docker compose -f scripts/techempower/postgres.yml down -v
# Python: requires the wheel installed in the active venv
# (cd python/mq-bridge-py && uv run maturin develop --release \
# --no-default-features -F http -F pyo3/extension-module)
scripts/techempower/verify.sh python
```
For a head-to-head throughput comparison against FastAPI/Sanic/etc., the
existing wrk harness still applies:
```bash
cd python/mq-bridge-py && uv run python analysis/bench_http_native.py
```
## Historical note (upstream is archived)
TechEmpower FrameworkBenchmarks was archived after Round 23, so there is no live
PR target anymore. The entries kept their submission shape (per-framework
`README.md`, `benchmark_config.json`, Dockerfile) so they remain a faithful,
runnable reference — and so they can still be dropped into a fork of the archived
repo to reproduce a round locally with `./tfb --mode verify --test mq-bridge
mq-bridge-py`. The Dockerfiles `git clone` this repo at `MQB_REF` (pin to a
released tag) and the Rust `Cargo.toml` path dependency can be swapped for a
`git`/crates.io release for reproducible image builds.
For maintained public suites, the equivalent (and PR-able) entries now live
under [`scripts/the-benchmarker/`](../the-benchmarker/) and
[`scripts/httparena/`](../httparena/).
> Honest framing: `mq-bridge-py` is the interesting entry (top-tier Python JSON
> throughput via off-GIL serialization). The Rust entry is a mid-pack
> hyper-based result — it optimizes for bridging/routing, not raw req/s.