Skip to content

express: json-tls, static-tls, crud and fortunes - #1247

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

express: json-tls, static-tls, crud and fortunes#1247
MDA2AV wants to merge 2 commits into
mainfrom
feat/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

The same app behind a node:https server on 8081. Every worker binds it exactly
as they all bind 8080, so the cluster shares the port rather than one process
carrying it — the failure mode #1245 fixed on quarkus.

express.static already streamed bodies from disk, so the static path needed no
change to take static-tls.

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

Rendered through a view engine, which is what standard mode requires: the
template is its own artifact at views/fortunes.hbs, and Handlebars escapes
{{ }} by default. The seed's row 11 carries a <script> tag and the profile
calls that escape its load-bearing check.

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=24754B
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 are the same app behind a node:https server on 8081.
Every worker binds it exactly as they all bind 8080, so the cluster shares the
port rather than one process carrying it.

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 renders through a view engine, which is what standard mode asks for -
the template is its own artifact at views/fortunes.hbs and Handlebars escapes
{{ }} by default. The seed's row 11 carries a <script> tag and the profile's
load-bearing check is that it leaves as text.

express.static already streamed bodies from disk, so static-tls needed no change
to the static path.

Validation: 64 passed, 0 failed.
@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: express | Test: all tests

Test Conn RPS CPU Mem Δ RPS Δ Mem
baseline 512 541,640 6569.9% 7.5GiB -10.9% +29.3%
baseline 4096 525,879 6444.3% 7.8GiB -11.3% +25.8%
pipelined 512 952,566 6609.6% 7.4GiB -5.6% +7.2%
pipelined 4096 942,872 6537.6% 8.5GiB -4.9% +18.1%
limited-conn 512 273,509 3983.3% 7.1GiB -1.7% +24.6%
limited-conn 4096 273,772 3853.3% 7.1GiB -2.8% +22.4%
json 4096 439,317 6546.9% 8.1GiB +11.1% +14.1%
json-comp 512 63,805 6452.4% 7.2GiB -14.7% +20.0%
json-comp 4096 51,498 6449.2% 7.2GiB -2.8% +22.0%
json-comp 16384 53,352 6238.4% 7.6GiB ~0% +13.4%
json-tls 4096 394,866 6495.1% 7.7GiB NEW NEW
upload 32 1,843 5277.9% 5.9GiB -9.1% ~0%
upload 256 1,906 6520.9% 6.5GiB -9.4% +8.3%
api-4 256 17,940 398.8% 464MiB -3.7% +10.5%
api-16 1024 64,182 1513.6% 1.7GiB -5.2% +6.2%
static 1024 24,261 6694.9% 5.9GiB +3.2% +11.3%
static 4096 25,169 6617.8% 16.7GiB +0.5% +3.7%
static 6800 24,454 6593.6% 18.7GiB -2.1% +4.5%
static-tls 1024 22,685 6640.4% 6.1GiB NEW NEW
static-tls 4096 22,915 6523.2% 17.3GiB NEW NEW
static-tls 6800 22,227 6469.4% 19.5GiB NEW NEW
async-db 1024 173,099 6439.7% 7.8GiB +2.0% +6.8%
crud 4096 275,733 6299.0% 8.6GiB NEW NEW
fortunes 1024 58,855 6491.6% 8.0GiB NEW NEW
Full log
=== Best: 173099 req/s (CPU: 6439.7%, Mem: 7.8GiB) ===
[info] input BW: 11.56MB/s (avg template: 70 bytes)
[info] saved results/async-db/1024/express.json
httparena-bench-express
httparena-bench-express

==============================================
=== 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   16.56ms   16.30ms   27.60ms   47.40ms   74.20ms

  3488558 requests in 15.00s, 3485208 responses
  Throughput: 232.30K req/s
  Bandwidth:  98.99MB/s
  Status codes: 2xx=3485208, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 3485207 / 3485208 responses (100.0%)
  Reconnects: 15469
  Per-template: 140418,152415,162921,171851,183082,186505,183323,182368,182021,183005,181972,182063,182963,182888,182244,183078,183818,180123,157438,140711
  Per-template-ok: 140418,152415,162921,171851,183082,186505,183323,182368,182021,183005,181972,182063,182963,182888,182244,183078,183818,180123,157438,140711
[info] CPU 6356.1% | Mem 8.8GiB

[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   13.94ms   14.80ms   21.90ms   33.60ms   52.70ms

  4100543 requests in 15.00s, 4097245 responses
  Throughput: 273.10K req/s
  Bandwidth:  115.71MB/s
  Status codes: 2xx=4097245, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 4097240 / 4097245 responses (100.0%)
  Reconnects: 18456
  Per-template: 180529,188750,190724,206060,217829,220498,215544,207958,208108,208601,208408,208108,207365,208082,207588,207283,212970,214522,194487,183826
  Per-template-ok: 180529,188750,190724,206060,217829,220498,215544,207958,208108,208601,208408,208108,207365,208082,207588,207283,212970,214522,194487,183826
[info] CPU 6340.0% | Mem 8.9GiB

[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   13.85ms   15.20ms   22.00ms   32.20ms   52.60ms

  4139025 requests in 15.00s, 4136002 responses
  Throughput: 275.69K req/s
  Bandwidth:  116.68MB/s
  Status codes: 2xx=4136002, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 4135999 / 4136002 responses (100.0%)
  Reconnects: 18747
  Per-template: 172047,185729,191184,202682,216184,226244,221757,215400,213369,213341,214500,213931,213309,214996,214897,214070,211627,208306,192503,179923
  Per-template-ok: 172047,185729,191184,202682,216184,226244,221757,215400,213369,213341,214500,213931,213309,214996,214897,214070,211627,208306,192503,179923
[info] CPU 6299.0% | Mem 8.6GiB

=== Best: 275733 req/s (CPU: 6299.0%, Mem: 8.6GiB) ===
[info] input BW: 23.67MB/s (avg template: 90 bytes)
[info] saved results/crud/4096/express.json
httparena-bench-express
httparena-bench-express

==============================================
=== 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   18.73ms   17.10ms   24.40ms   47.60ms   138.10ms

  254133 requests in 5.00s, 253944 responses
  Throughput: 50.77K req/s
  Bandwidth:  1.19GB/s
  Status codes: 2xx=253944, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 253944 / 253944 responses (100.0%)
[info] CPU 6468.2% | Mem 7.2GiB

[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   16.42ms   15.80ms   20.30ms   34.40ms   56.40ms

  284995 requests in 5.00s, 284856 responses
  Throughput: 56.94K req/s
  Bandwidth:  1.33GB/s
  Status codes: 2xx=284856, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 284854 / 284856 responses (100.0%)
[info] CPU 6482.4% | Mem 7.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   15.88ms   15.30ms   19.30ms   36.60ms   56.60ms

  294400 requests in 5.00s, 294276 responses
  Throughput: 58.83K req/s
  Bandwidth:  1.38GB/s
  Status codes: 2xx=294276, 3xx=0, 4xx=0, 5xx=0
  Latency samples: 294275 / 294276 responses (100.0%)
[info] CPU 6491.6% | Mem 8.0GiB

=== Best: 58855 req/s (CPU: 6491.6%, Mem: 8.0GiB) ===
[info] saved results/fortunes/1024/express.json
httparena-bench-express
httparena-bench-express
[info] skip: express does not subscribe to baseline-h2
[info] skip: express does not subscribe to static-h2
[info] skip: express does not subscribe to baseline-h2c
[info] skip: express does not subscribe to json-h2c
[info] skip: express does not subscribe to baseline-h3
[info] skip: express does not subscribe to static-h3
[info] skip: express does not subscribe to gateway-64
[info] skip: express does not subscribe to gateway-h3
[info] skip: express does not subscribe to production-stack
[info] skip: express does not subscribe to unary-grpc
[info] skip: express does not subscribe to unary-grpc-tls
[info] skip: express does not subscribe to stream-grpc
[info] skip: express does not subscribe to stream-grpc-tls
[info] skip: express does not subscribe to echo-ws
[info] skip: express does not subscribe to echo-ws-pipeline
[info] skip: 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/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