Skip to content

ultimate-express: json-tls, static-tls, crud and fortunes - #1246

Open
MDA2AV wants to merge 2 commits into
mainfrom
feat/ultimate-express-missing-profiles
Open

ultimate-express: json-tls, static-tls, crud and fortunes#1246
MDA2AV wants to merge 2 commits into
mainfrom
feat/ultimate-express-missing-profiles

Conversation

@MDA2AV

@MDA2AV MDA2AV commented Aug 21, 2026

Copy link
Copy Markdown
Owner

The four HTTP/1.1 profiles the entry was not subscribed to. Tests go from 10 to 14.

json-tls / static-tls

ultimate-express takes TLS material through the uWS options at construction
rather than through a separate https server, so 8081 is a second express()
carrying the same routes. The route table moved into registerRoutes() and both
apps are built from it — no second copy of the handlers.

After #1245 I checked this the same way rather than trusting validation, which is
correctness-only and cannot see a listener pinned to one process. Every worker in
the cluster binds 8081 exactly as they all bind 8080:

port rps (/json/50, c=256) workers burning CPU
8080 plaintext 751628 32 / 32
8081 TLS 618825 32 / 32

static-tls with the profile's own static-rotate.lua does 235318 rps.

crud

Cache-aside on Redis through ioredis, one connection per worker so the cache is
shared across the cluster rather than being a per-worker map, 200ms TTL, explicit
delete on update. express.json() is mounted on the two crud routes that carry a
body rather than globally — /upload takes a 20MB body it counts as it streams,
and a global parser would buffer and parse it.

fortunes

Emitted by hand rather than through a template engine, which tuned mode
explicitly allows. The handler still queries per request, appends the runtime
row, sorts ordinally and escapes < > & " ' — the seed's row 11 carries
a <script> tag and the profile's load-bearing check is that it leaves as text.

The existing static handler already read bodies from disk on every request, so it
needed no change to take static-tls.

Validation

64 passed, 0 failed, including all four new profiles:

json-tls    protocol negotiation, response schema, Content-Type
static-tls  20 files verified, 15 compressed / 5 skipped, 404 path
fortunes    <tr> count=202, runtime row, XSS escape, body size=24437B
crud        list, read, cache-aside MISS->HIT, 404, POST 201, PUT + invalidate

🤖 Generated with Claude Code

The four HTTP/1.1 profiles the entry was not subscribed to.

json-tls and static-tls listen on 8081. ultimate-express takes TLS material
through the uWS options at construction rather than through a separate https
server, so the port is a second express() carrying the same routes - the route
table moved into registerRoutes() and both apps are built from it. Every worker
in the cluster binds 8081 the same way they all bind 8080, so the listener is
spread rather than parked on one process; measured at 32/32 workers burning CPU
on both ports, 618k rps over TLS against 751k plaintext for /json/50.

crud runs cache-aside on Redis through ioredis, one connection per worker so the
cache is shared across the cluster, with a 200ms TTL and an explicit delete on
update. express.json() is mounted on the two crud routes that carry a body
rather than globally, so /upload keeps counting its 20MB body as it streams
instead of having it buffered and parsed.

fortunes is emitted by hand rather than through a template engine, which tuned
mode allows. The handler still queries per request, appends the runtime row,
sorts ordinally and escapes < > & " ' - the seed's row 11 carries a <script>
tag and the profile's load-bearing check is that it leaves as text.

Validation: 64 passed, 0 failed.
@MDA2AV
MDA2AV force-pushed the feat/ultimate-express-missing-profiles branch from ddef876 to 7742545 Compare August 21, 2026 22:35
@MDA2AV

MDA2AV commented Aug 21, 2026

Copy link
Copy Markdown
Owner Author

/benchmark --save

@github-actions

Copy link
Copy Markdown
Contributor

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

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results

Framework: ultimate-express | Test: all tests

Test Conn RPS CPU Mem Δ RPS Δ Mem
baseline 512 1,179,079 6735.8% 7.2GiB -2.0% -1.4%
baseline 4096 1,169,575 6734.7% 7.7GiB -2.1% +1.3%
pipelined 512 4,739,491 6519.2% 7.9GiB +0.9% +1.3%
pipelined 4096 4,451,574 6380.7% 8.2GiB -3.5% +3.8%
limited-conn 512 1,020,005 6166.4% 7.5GiB -0.8% ~0%
limited-conn 4096 1,085,526 6613.9% 7.6GiB -1.0% ~0%
json 4096 905,560 6593.1% 7.6GiB -0.6% ~0%
json-comp 512 352,033 6163.3% 8.0GiB -9.6% -2.4%
json-comp 4096 355,458 6352.6% 8.9GiB -7.6% +2.3%
json-comp 16384 375,419 6316.3% 10.2GiB -6.4% +2.0%
json-tls 4096 843,414 6607.3% 7.5GiB NEW NEW
upload 32 2,027 2917.5% 6.6GiB -0.3% -1.5%
upload 256 2,133 6315.8% 7.2GiB -0.5% ~0%
api-4 256 32,198 397.0% 461MiB +0.2% -5.1%
api-16 1024 115,417 1536.9% 1.9GiB +0.5% ~0%
static 1024 416,771 6514.7% 8.4GiB +0.9% -2.3%
static 4096 429,741 6520.6% 8.3GiB -1.5% ~0%
static 6800 419,931 6507.7% 8.4GiB -1.3% ~0%
static-tls 1024 345,081 6533.4% 8.3GiB NEW NEW
static-tls 4096 364,199 6443.0% 8.2GiB NEW NEW
static-tls 6800 353,484 6415.6% 8.4GiB NEW NEW
async-db 1024 184,685 6149.2% 8.2GiB -1.7% +3.8%
crud 4096 342,355 5276.5% 8.4GiB NEW NEW
fortunes 1024 81,600 6546.4% 6.8GiB NEW NEW
Full log
=== Best: 184685 req/s (CPU: 6149.2%, Mem: 8.2GiB) ===
[info] input BW: 12.33MB/s (avg template: 70 bytes)
[info] saved results/async-db/1024/ultimate-express.json
httparena-bench-ultimate-express
httparena-bench-ultimate-express

==============================================
=== ultimate-express / crud / 4096c (tool=gcannon) ===
==============================================
[info] resetting postgres for a clean per-profile baseline
[info] starting postgres sidecar
httparena-postgres
[info] postgres ready (seeded)
[info] waiting for server...
[info] server ready

[run 1/3]
gcannon v0.5.3
  Target:    localhost:8080/
  Threads:   64
  Conns:     4096 (64/thread)
  Pipeline:  1
  Req/conn:  200
  Templates: 20
  Expected:  200
  Duration:  15s


  Thread Stats   Avg      p50      p90      p99    p99.9
    Latency   12.66ms   12.50ms   18.30ms   32.90ms   53.80ms

  4845041 requests in 15.00s, 4844780 responses
  Throughput: 322.94K req/s
  Bandwidth:  131.32MB/s
  Status codes: 2xx=4844780, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 4844780 / 4844780 responses (100.0%)
  Reconnects: 22246
  Per-template: 277720,282902,263077,245878,248200,245250,227303,226551,226650,226916,226677,226237,227154,227267,226637,226457,244946,259900,256137,252921
  Per-template-ok: 277720,282902,263077,245878,248200,245250,227303,226551,226650,226916,226677,226237,227154,227267,226637,226457,244946,259900,256137,252921
[info] CPU 5586.6% | Mem 8.7GiB

[run 2/3]
gcannon v0.5.3
  Target:    localhost:8080/
  Threads:   64
  Conns:     4096 (64/thread)
  Pipeline:  1
  Req/conn:  200
  Templates: 20
  Expected:  200
  Duration:  15s


  Thread Stats   Avg      p50      p90      p99    p99.9
    Latency   11.97ms   12.30ms   16.60ms   24.50ms   35.00ms

  5111903 requests in 15.00s, 5111892 responses
  Throughput: 340.73K req/s
  Bandwidth:  137.36MB/s
  Status codes: 2xx=5111892, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 5111884 / 5111892 responses (100.0%)
  Reconnects: 23083
  Per-template: 283481,295516,294153,266092,263296,264722,258081,237427,238503,238347,237729,237258,237156,237678,237517,237556,245001,271235,268391,262745
  Per-template-ok: 283481,295516,294153,266092,263296,264722,258081,237427,238503,238347,237729,237258,237156,237678,237517,237556,245001,271235,268391,262745
[info] CPU 5359.7% | Mem 8.4GiB

[run 3/3]
gcannon v0.5.3
  Target:    localhost:8080/
  Threads:   64
  Conns:     4096 (64/thread)
  Pipeline:  1
  Req/conn:  200
  Templates: 20
  Expected:  200
  Duration:  15s


  Thread Stats   Avg      p50      p90      p99    p99.9
    Latency   11.92ms   12.20ms   16.50ms   24.90ms   33.90ms

  5135511 requests in 15.00s, 5135327 responses
  Throughput: 342.31K req/s
  Bandwidth:  137.93MB/s
  Status codes: 2xx=5135327, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 5135318 / 5135327 responses (100.0%)
  Reconnects: 23129
  Per-template: 282655,298751,296960,266849,265391,267597,255953,239355,239517,239182,238757,238810,238961,239024,239184,238906,245170,271460,267729,265107
  Per-template-ok: 282655,298751,296960,266849,265391,267597,255953,239355,239517,239182,238757,238810,238961,239024,239184,238906,245170,271460,267729,265107
[info] CPU 5276.5% | Mem 8.4GiB

=== Best: 342355 req/s (CPU: 5276.5%, Mem: 8.4GiB) ===
[info] input BW: 29.38MB/s (avg template: 90 bytes)
[info] saved results/crud/4096/ultimate-express.json
httparena-bench-ultimate-express
httparena-bench-ultimate-express

==============================================
=== ultimate-express / fortunes / 1024c (tool=gcannon) ===
==============================================
[info] resetting postgres for a clean per-profile baseline
[info] starting postgres sidecar
httparena-postgres
[info] postgres ready (seeded)
[info] waiting for server...
[info] server ready

[run 1/3]
gcannon v0.5.3
  Target:    localhost:8080/fortunes
  Threads:   64
  Conns:     1024 (16/thread)
  Pipeline:  1
  Req/conn:  unlimited (keep-alive)
  Expected:  200
  Duration:  5s


  Thread Stats   Avg      p50      p90      p99    p99.9
    Latency   12.62ms   11.90ms   17.90ms   31.30ms   54.50ms

  375053 requests in 5.00s, 375053 responses
  Throughput: 74.98K req/s
  Bandwidth:  1.73GB/s
  Status codes: 2xx=375053, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 375052 / 375053 responses (100.0%)
[info] CPU 6447.2% | Mem 6.5GiB

[run 2/3]
gcannon v0.5.3
  Target:    localhost:8080/fortunes
  Threads:   64
  Conns:     1024 (16/thread)
  Pipeline:  1
  Req/conn:  unlimited (keep-alive)
  Expected:  200
  Duration:  5s


  Thread Stats   Avg      p50      p90      p99    p99.9
    Latency   11.33ms   10.80ms   15.80ms   21.80ms   37.40ms

  408004 requests in 5.00s, 408004 responses
  Throughput: 81.57K req/s
  Bandwidth:  1.89GB/s
  Status codes: 2xx=408004, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 408003 / 408004 responses (100.0%)
[info] CPU 6546.4% | Mem 6.8GiB

[run 3/3]
gcannon v0.5.3
  Target:    localhost:8080/fortunes
  Threads:   64
  Conns:     1024 (16/thread)
  Pipeline:  1
  Req/conn:  unlimited (keep-alive)
  Expected:  200
  Duration:  5s


  Thread Stats   Avg      p50      p90      p99    p99.9
    Latency   11.46ms   11.00ms   15.50ms   26.30ms   42.90ms

  404194 requests in 5.00s, 404194 responses
  Throughput: 80.80K req/s
  Bandwidth:  1.87GB/s
  Status codes: 2xx=404194, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 404190 / 404194 responses (100.0%)
[info] CPU 6499.2% | Mem 7.1GiB

=== Best: 81600 req/s (CPU: 6546.4%, Mem: 6.8GiB) ===
[info] saved results/fortunes/1024/ultimate-express.json
httparena-bench-ultimate-express
httparena-bench-ultimate-express
[info] skip: ultimate-express does not subscribe to baseline-h2
[info] skip: ultimate-express does not subscribe to static-h2
[info] skip: ultimate-express does not subscribe to baseline-h2c
[info] skip: ultimate-express does not subscribe to json-h2c
[info] skip: ultimate-express does not subscribe to baseline-h3
[info] skip: ultimate-express does not subscribe to static-h3
[info] skip: ultimate-express does not subscribe to gateway-64
[info] skip: ultimate-express does not subscribe to gateway-h3
[info] skip: ultimate-express does not subscribe to production-stack
[info] skip: ultimate-express does not subscribe to unary-grpc
[info] skip: ultimate-express does not subscribe to unary-grpc-tls
[info] skip: ultimate-express does not subscribe to stream-grpc
[info] skip: ultimate-express does not subscribe to stream-grpc-tls
[info] skip: ultimate-express does not subscribe to echo-ws
[info] skip: ultimate-express does not subscribe to echo-ws-pipeline
[info] skip: ultimate-express does not subscribe to echo-ws-limited
[info] rebuilding site/data/*.json
[purged stale] zix-grpc.json (zix-grpc)
[purged stale] zix-ws.json (zix-ws)
[updated] /home/diogo/actions-runner/_work/HttpArena/HttpArena/site/data/frameworks.json
[updated] /home/diogo/actions-runner/_work/HttpArena/HttpArena/site/data/results/ultimate-express.json - 24 new, 24 total
[updated] /home/diogo/actions-runner/_work/HttpArena/HttpArena/site/data/current.json
[info] done
httparena-postgres
httparena-redis
[info] restoring loopback MTU to 65536

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